赞
踩
目录
② 不需要CPU参与,PPI的方式即把组的开启关闭当做是一个PPI的任务,通过PPI事件触发
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 – nRF52820Softdevice命名规则一。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
其中 :
Advanced High-performance Bus(AHB) runs at 64MHz, but the Advanced Peripheral Bus(APB) runs at 16MHz.
64 MHz晶体振荡器(HFXO)由32 MHz外部晶振控制,再经过PLL提供48Mhz给USB
NRF52832时钟控制系统_f78fk_liuyu的博客-CSDN博客
Nordic的引脚是可以自由定义的。只有SAADC接口是固定的那几个引脚,
数字引脚,PWM, I2C, UART, I2S都是可以自由定义的。
58233的系统时钟固定在64Mhz不可变
// <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博客
可以将部分52840例程在52833上跑(nRF52833 是 nRF52840 的子集)
如果移植不同的板,我应该改变什么?- 北欧问答 - 北欧开发区 - 北欧开发区 (nordicsemi.com)
nRF52833-DK 眨眼问题 - 北欧问答 - 北欧开发区 - 北欧开发区 (nordicsemi.com)
我可以将 PCA 10056 的示例代码用于 PCA 10100 吗?- 北欧问答 - 北欧开发区 - 北欧开发区 (nordicsemi.com)
(初始化实例→初始化外设结构体配置→初始化设备→触发)
第一种赋值方式
第二种赋值方式(typedef uint16_t nrf_pwm_values_common_t;)
- 用的是nrf_drv_pwm_complex_playback不是nrf_drv_pwm_simple_playback();
-
-
-
- /********************seq0 渐变**********************/
- uint16_t value = 0;
- uint8_t i;
- //设置序列0的占空比,这个数组不能在堆栈上分配(因此是“静态的”),他必须在RAM中
- static nrf_pwm_values_common_t seq0_values[25];
- for (i = 0;i < 25 ; ++i)
- {
- value += 25000/25;
- seq0_values[i] = value;
- }
-
- //设置序列
- nrf_pwm_sequence_t const seq0 =
- {
- .values.p_common = seq0_values,
- .length = NRF_PWM_VALUES_LENGTH(seq0_values),
- .repeats = 0,
- .end_delay = 0
- };
-
- /********************seq1 暗灭**********************/
- static nrf_pwm_values_common_t seq1_values[] =
- {
- 0,
- 0x8000,
- 0,
- 0x8000,
- };
- nrf_pwm_sequence_t const seq1 =
- {
- .values.p_common = seq1_values,
- .length = NRF_PWM_VALUES_LENGTH(seq1_values),
- .repeats = 4,
- .end_delay = 0
- };
-
- (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);
一个PWM模块的四个通道各自一个序列,实现4个LED依次亮灭
- static void my_demo(void)
- {
-
- nrf_drv_pwm_config_t const my_config =
- {
- .output_pins =
- {
- BSP_LED_0 | NRF_DRV_PWM_PIN_INVERTED,
- BSP_LED_1 | NRF_DRV_PWM_PIN_INVERTED,
- BSP_LED_2 | NRF_DRV_PWM_PIN_INVERTED,
- BSP_LED_3 | NRF_DRV_PWM_PIN_INVERTED,
- },
- .irq_priority = APP_IRQ_PRIORITY_LOWEST,
- .base_clock = NRF_PWM_CLK_1MHz,
- .count_mode = NRF_PWM_MODE_UP,
- .top_value = 25000,
- .load_mode = NRF_PWM_LOAD_INDIVIDUAL,
- .step_mode = NRF_PWM_STEP_AUTO
- };
- APP_ERROR_CHECK(nrf_drv_pwm_init(&m_pwm0,&my_config,NULL));
-
- static nrf_pwm_values_individual_t seq1_values[] =
- {
- {0x8000,0,0,0},
- {0,0x8000,0,0},
- {0,0,0x8000,0},
- {0,0,0,0x8000}
- };
- nrf_pwm_sequence_t const seq1 =
- {
- .values.p_individual = seq1_values,
- .length = NRF_PWM_VALUES_LENGTH(seq1_values),
- .repeats = 10,
- .end_delay = 0
- };
-
- (void)nrf_drv_pwm_simple_playback(&m_pwm0,&seq1,1,NRF_DRV_PWM_FLAG_LOOP);
- }
在回调函数里面对值的修改,该回调函数值得学习借鉴。
- /*************************************************************************/
-
- static nrf_pwm_values_individual_t my_demo1_seq_values;
- static uint8_t my_demo1_phase;
- static uint16_t const my_demo1_step = 200;
- static uint16_t const my_demo1_top = 10000;
- static nrf_pwm_sequence_t const my_demo1_seq =
- {
- .values.p_individual = &my_demo1_seq_values,
- .length = NRF_PWM_VALUES_LENGTH(my_demo1_seq_values),
- .repeats = 0,
- .end_delay = 0
- };
-
- static void my_demo1_handler(nrf_drv_pwm_evt_type_t event_type)
- {
- if (event_type == NRF_DRV_PWM_EVT_FINISHED)
- {
- //my_demol_phase 每次+1 后进行右移00,01,10,11,110,111 变为00(通道0递增),00(通道0递减),01(通道1递增),01(通道1递减),010,010,011,011
- uint8_t channel = my_demo1_phase >> 1;
- bool down = my_demo1_phase & 1; //0(递增),1(递减),0,1,0,1
- bool next_phase = false;
-
- uint16_t * p_channels = (uint16_t *)&my_demo1_seq_values;
- uint16_t value = p_channels[channel];
- if (down) //Decrement
- {
- value -= my_demo1_step;
- if (value == 0)
- {
- next_phase = true;
- }
- }
- else //Increase
- {
- value += my_demo1_step;
- if (value >= my_demo1_top)
- {
- next_phase = true;
- }
- }
- p_channels[channel] = value;
-
- if (next_phase)
- {
- if (++my_demo1_phase >= 2 * NRF_PWM_CHANNEL_COUNT)
- {
- my_demo1_phase = 0;
- }
- }
- }
- }
-
-
- static void my_demo(void)
- {
-
- #if 1 //独立模式含回调
-
-
- nrf_drv_pwm_config_t const my_config =
- {
- .output_pins =
- {
- BSP_LED_0 | NRF_DRV_PWM_PIN_INVERTED,
- BSP_LED_1 | NRF_DRV_PWM_PIN_INVERTED,
- BSP_LED_2 | NRF_DRV_PWM_PIN_INVERTED,
- BSP_LED_3 | NRF_DRV_PWM_PIN_INVERTED,
- },
- .irq_priority = APP_IRQ_PRIORITY_LOWEST,
- .base_clock = NRF_PWM_CLK_1MHz,
- .count_mode = NRF_PWM_MODE_UP,
- .top_value = my_demo1_top,
- .load_mode = NRF_PWM_LOAD_INDIVIDUAL,
- .step_mode = NRF_PWM_STEP_AUTO
- };
- APP_ERROR_CHECK(nrf_drv_pwm_init(&m_pwm0,&my_config,my_demo1_handler));
-
- //占空比值和极性的初始化
- my_demo1_seq_values.channel_0 = 0;
- my_demo1_seq_values.channel_1 = 0;
- my_demo1_seq_values.channel_2 = 0;
- my_demo1_seq_values.channel_3 = 0;
- my_demo1_phase = 0;
-
- (void)nrf_drv_pwm_simple_playback(&m_pwm0,&my_demo1_seq,1,NRF_DRV_PWM_FLAG_LOOP);
-
- }
效果渐亮到渐暗,然后下一个LED
- static void my_demo(void)
- {
-
- #if 1 //分组加载模式
-
- nrf_drv_pwm_config_t my_config =
- {
- .irq_priority = APP_IRQ_PRIORITY_LOWEST,
-
- .count_mode = NRF_PWM_MODE_UP,
-
- .step_mode = NRF_PWM_STEP_AUTO
- };
-
- my_config.output_pins[0] = BSP_LED_0 | NRF_DRV_PWM_PIN_INVERTED;
- my_config.output_pins[1] = BSP_LED_1 | NRF_DRV_PWM_PIN_INVERTED;
- my_config.output_pins[2] = BSP_LED_2 | NRF_DRV_PWM_PIN_INVERTED;
- my_config.output_pins[3] = BSP_LED_3 | NRF_DRV_PWM_PIN_INVERTED;
- my_config.base_clock = NRF_PWM_CLK_1MHz;
- my_config.top_value = my_demo1_top;
- my_config.load_mode = NRF_PWM_LOAD_GROUPED;
-
- APP_ERROR_CHECK(nrf_drv_pwm_init(&m_pwm0,&my_config,NULL));
-
- static nrf_pwm_values_grouped_t seq1_values[] =
- {
- //组1:灭亮灭亮
- //组2:亮灭亮灭
- {0,0x8000},
- {0x8000,0},
- {0,0x8000},
- {0x8000,0}
- };
- nrf_pwm_sequence_t const seq1 =
- {
- .values.p_grouped = seq1_values,
- .length = NRF_PWM_VALUES_LENGTH(seq1_values),
- .repeats = 100,
- .end_delay = 0
- };
- (void)nrf_drv_pwm_simple_playback(&m_pwm0,&seq1,1,NRF_DRV_PWM_FLAG_LOOP);
-
- #endif
- }
现象LED1、2为一组 LED3、4为一组
//组1:灭亮灭亮//组2:亮灭亮灭
- static void my_demo(void)
- {
-
- #if 1 //波形模式
- nrf_drv_pwm_config_t const my_config =
- {
- .output_pins =
- {
- BSP_LED_0 | NRF_DRV_PWM_PIN_INVERTED,
- NRF_DRV_PWM_PIN_NOT_USED,
- NRF_DRV_PWM_PIN_NOT_USED,
- },
- .irq_priority = APP_IRQ_PRIORITY_LOWEST,
- .base_clock = NRF_PWM_CLK_1MHz,
- .count_mode = NRF_PWM_MODE_UP,
- //.top_value = 25000, 顶点值可以不用配置
- .load_mode = NRF_PWM_LOAD_WAVE_FORM,
- .step_mode = NRF_PWM_STEP_AUTO
- };
- APP_ERROR_CHECK(nrf_drv_pwm_init(&m_pwm0,&my_config,NULL));
- m_used |= USED_PWM(0); //使用PWM0模块
-
- //ram中包含占极性、空比、顶点值
- static nrf_pwm_values_wave_form_t seq1_values[] =
- {
- {0,0,0,0x4d09}, //0x4d09周期1
- {0x8000,0,0,0x4d09},
-
- {0,0,0,0x3d09}, //0x3d09周期2
- {0x8000,0,0,0x3d09},
-
- {0,0,0,0x1d09}, //0x1d09周期3
- {0x8000,0,0,0x1d09},
- };
-
- //设置序列
- nrf_pwm_sequence_t const seq0 =
- {
- .values.p_wave_form = seq1_values,
- .length = NRF_PWM_VALUES_LENGTH(seq1_values),
- .repeats = 0,
- .end_delay = 0
- };
-
- (void)nrf_drv_pwm_simple_playback(&m_pwm0,&seq0,1,NRF_DRV_PWM_FLAG_LOOP);
-
-
- #endif
- }
效果实现变周期
fTIMER是定时器的定时频率即定时周期的倒数,模块会根据这个值来自动选择输入时钟
配置ms级的定时器
- #include <stdbool.h>
- #include <stdint.h>
- #include "nrf.h"
- #include "nrf_drv_timer.h"
- #include "bsp.h"
- #include "app_error.h"
-
- const nrf_drv_timer_t TIMER_LED = NRF_DRV_TIMER_INSTANCE(0); //配置哪一个定时器这里配置TIMER0
-
- /**
- * @brief Handler for timer events. //中断事件这里为比较事件
- */
- void timer_led_event_handler(nrf_timer_event_t event_type, void* p_context)
- {
- static uint32_t i;
- uint32_t led_to_invert = ((i++) % LEDS_NUMBER);
-
- switch (event_type)
- {
- case NRF_TIMER_EVENT_COMPARE0: //比较事件计时结束
- bsp_board_led_invert(led_to_invert);
- break;
-
- default:
- //Do nothing.
- break;
- }
- }
-
-
- /**
- * @brief Function for main application entry.
- */
- int main(void)
- {
- uint32_t time_ms = 500; //Time(in miliseconds) between consecutive compare events.//定时器比较事件事件的时间
- uint32_t time_ticks;
- uint32_t err_code = NRF_SUCCESS;
-
- //Configure all leds on board.
- bsp_board_init(BSP_INIT_LEDS);
-
- //Configure TIMER_LED for generating simple light effect - leds on board will invert his state one after the other.
- nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG; //定时器结构体默认配置
- err_code = nrf_drv_timer_init(&TIMER_LED, &timer_cfg, timer_led_event_handler); //初始化定时器
- APP_ERROR_CHECK(err_code);
- //计算CC寄存器中的值
- time_ticks = nrf_drv_timer_ms_to_ticks(&TIMER_LED, time_ms);
- //触发定时器比较
- nrf_drv_timer_extended_compare(
- &TIMER_LED, NRF_TIMER_CC_CHANNEL0, time_ticks, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, true);
- //NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK 快捷方式会清除定时器的counter从而从新开始计数
- nrf_drv_timer_enable(&TIMER_LED);
-
- while (1)
- {
- __WFI();
- }
- }
eg:当按键按下会产生一个相应的中断,并在中断处理事件
- void giop_inint_test(void);
-
- int main(void)
- {
- giop_inint_test();
- while (true)
- {
- // Do Nothing - GPIO can be toggled without software intervention.
- }
- }
- void in_pin_handeer(nrf_drv_gpiote_pin_t pin,nrf_gpiote_polarity_t active)
- {
- if(nrf_gpio_pin_read(BUTTON_1) == 0) //按键消抖
- {
- nrf_gpio_pin_toggle(BSP_LED_0);
- }
- }
- void giop_inint_test(void)
- {
- nrf_gpio_cfg_output(BSP_LED_0);
-
- nrf_drv_gpiote_init();
- nrf_drv_gpiote_in_config_t in_config = GPIOTE_CONFIG_IN_SENSE_TOGGLE(true);
- in_config.pull = NRF_GPIO_PIN_PULLUP;
- //设置GPIOTE输入,极性,模式
- //in_pin_handeer为回调函数,中断函数在nrf_drv_gpiote_in_init内部,发生中断后会调用回调函数
- nrf_drv_gpiote_in_init(BUTTON_1,&in_config,in_pin_handeer);
-
- nrf_drv_gpiote_in_event_enable(BUTTON_1,true);
- }
PORT模式应对GPIOTE只能绑定8个通道的问题(可以32个IO口通用一个通道)
- void giop_inint_test(void);
- int main(void)
- {
- giop_inint_test();
- while (true)
- {
- // Do Nothing - GPIO can be toggled without software intervention.
- }
- }
- void in_pin_handeer(nrf_drv_gpiote_pin_t pin,nrf_gpiote_polarity_t active)
- {
- if(pin == BUTTON_1) //按键消抖
- {
- nrf_gpio_pin_toggle(BSP_LED_0);
- }
- else if(pin == BUTTON_2) //按键消抖
- {
- nrf_gpio_pin_toggle(BSP_LED_1);
- }
- else if(pin == BUTTON_3) //按键消抖
- {
- nrf_gpio_pin_toggle(BSP_LED_2);
- }
- else if(pin == BUTTON_4) //按键消抖
- {
- nrf_gpio_pin_toggle(BSP_LED_3);
- }
- }
- void giop_inint_test(void)
- {
- nrf_gpio_cfg_output(BSP_LED_0);
- nrf_gpio_cfg_output(BSP_LED_1);
- nrf_gpio_cfg_output(BSP_LED_2);
- nrf_gpio_cfg_output(BSP_LED_3);
-
- nrf_drv_gpiote_init();
- //配置SENSE模式,选择false为sense配置
- nrf_drv_gpiote_in_config_t in_config = GPIOTE_CONFIG_IN_SENSE_HITOLO(true);
- in_config.pull = NRF_GPIO_PIN_PULLUP;
- //设置GPIOTE输入,极性,模式
- //in_pin_handeer为回调函数,中断函数在nrf_drv_gpiote_in_init内部,发生中断后会调用回调函数
-
- //配置按键0绑定POTR
- nrf_drv_gpiote_in_init(BUTTON_1,&in_config,in_pin_handeer);
- nrf_drv_gpiote_in_event_enable(BUTTON_1,true);
- //配置按键1绑定POTR
- nrf_drv_gpiote_in_init(BUTTON_2,&in_config,in_pin_handeer);
- nrf_drv_gpiote_in_event_enable(BUTTON_2,true);
- //配置按键2绑定POTR
- nrf_drv_gpiote_in_init(BUTTON_3,&in_config,in_pin_handeer);
- nrf_drv_gpiote_in_event_enable(BUTTON_3,true);
- //配置按键3绑定POTR
- nrf_drv_gpiote_in_init(BUTTON_4,&in_config,in_pin_handeer);
- nrf_drv_gpiote_in_event_enable(BUTTON_4,true);
- }
eg:给LED灯绑定一个任务,当别的方法触发时LED做相应的电平变化(见下下图),可以同时触发多个(32个都可以?)
- int main(void)
- {
-
- giop_inint_test();
- nrf_gpio_cfg_output(BSP_LED_0);
- //初始化GPIOTE模块
- nrf_drv_gpiote_init();
- //定义GPIOTE输出初始化结构体,主要配置为翻转模式
- nrf_drv_gpiote_out_config_t out_config = GPIOTE_CONFIG_OUT_TASK_TOGGLE(true);
- //给指定的GPIO口绑定任务
- nrf_drv_gpiote_out_init(BSP_LED_0,&out_config);
- //开始任务
- nrf_drv_gpiote_out_task_enable(BSP_LED_0);
-
-
-
-
- #endif
-
-
- while (true)
- {
- //不断触发任务
- nrf_drv_gpiote_out_task_trigger(BSP_LED_0);
- // Do Nothing - GPIO can be toggled without software intervention.
- }
- }
-
- void in_pin_handeer(nrf_drv_gpiote_pin_t pin,nrf_gpiote_polarity_t active)
- {
- if(pin == BUTTON_1) //按键消抖
- {
- //触发任务
- nrf_drv_gpiote_out_task_trigger(BSP_LED_0);
- }
- else if(pin == BUTTON_2) //按键消抖
- {
- nrf_gpio_pin_toggle(BSP_LED_1);
- }
- else if(pin == BUTTON_3) //按键消抖
- {
- nrf_gpio_pin_toggle(BSP_LED_2);
- }
- else if(pin == BUTTON_4) //按键消抖
- {
- nrf_gpio_pin_toggle(BSP_LED_3);
- }
- }
配合上面的事件模式 ,实现按下按键,触发任务翻转LED
① Programmable peripheral interconnect(PPI):即不通过CPU、中断(GPIOTE是触发中断)由一个事件触发一个任务。
② 共32个通道,可编程的20个通道,12个固定的事件任务对。
③ PPI通道可进行分组,将多个PPI通道分为一组进行统一管理,同时打开或者关闭组中的所以PPI通道,最多可实现6个组。
一个事件触发一人组中的任务实现一对多。统一开启组里面对应的PPI。④ fork 从任务,即一个事件可以触发两个任务,一个主任务,一个从任务。
⑤ 配合GPIOTE食用
使用的基本配置流程
eg :按键触发LED寄存器版
库函数
- void my_ppi_init(void);
-
- void gpiote_init(void);
-
- int main(void)
- {
-
- gpiote_init();
- my_ppi_init();
-
- while(true);
-
- }
-
- static nrf_ppi_channel_t my_ppi_channel;
-
- void gpiote_init(void)
- {
- ret_code_t err_code;
- //初始化GPIOTE
- err_code = nrf_drv_gpiote_init();
- APP_ERROR_CHECK(err_code);
-
- //配置LED端口翻转输出任务
- nrf_drv_gpiote_out_config_t out_config = GPIOTE_CONFIG_OUT_TASK_TOGGLE(true);
- //绑定输出端口
- err_code = nrf_drv_gpiote_out_init(BSP_LED_0,&out_config);
- //配置为输出端口任务使能
- nrf_drv_gpiote_out_task_enable(BSP_LED_0);
-
- //配置按键端口高电平变低电平事件
- nrf_drv_gpiote_in_config_t in_config = GPIOTE_CONFIG_IN_SENSE_HITOLO(true);
- in_config.pull = NRF_GPIO_PIN_PULLUP;
- //绑定输入端口
- err_code = nrf_drv_gpiote_in_init(BUTTON_1,&in_config,NULL);
- APP_ERROR_CHECK(err_code);
- //配置输入事件使能
- nrf_drv_gpiote_in_event_enable(BUTTON_1,true);
-
- }
- void my_ppi_init(void)
- {
-
- ret_code_t err_code;
- APP_ERROR_CHECK(err_code);
-
-
- //初始化PPI模块
- err_code = nrf_drv_ppi_init();
- APP_ERROR_CHECK(err_code);
-
- //配置PPI的频道
- err_code = nrfx_ppi_channel_alloc(&my_ppi_channel);
- APP_ERROR_CHECK(err_code);
-
- //设置PPI通道的EPP(事件)和TEP(任务) 两端对应的端口
- err_code = nrfx_ppi_channel_assign(my_ppi_channel,
- nrfx_gpiote_in_event_addr_get(BUTTON_1),
- nrfx_gpiote_out_task_addr_get(BSP_LED_0));
- APP_ERROR_CHECK(err_code);
-
- //使能PPI通道
- err_code = nrfx_ppi_channel_enable(my_ppi_channel);
- APP_ERROR_CHECK(err_code);
-
- }
GROUP 组的开启关闭配置
上述是吧按键1与LED0,按键2与LED1,各设置一组PPI通道0和通道1,再把通道0和通道1绑定的到PPI group0上。但按键3按下使能组(未使能前按下按键1或2无响应),使能后里面两个PPI通道就可以用,按键1按下LED0翻转,按键2按下LED1翻转。按键4按下使能组
库函数
- static nrf_ppi_channel_t my_ppi_channel;
- static nrf_ppi_channel_t my_ppi_channel_1;
- static nrf_ppi_channel_group_t my_ppi_group;
- int main(void)
- {
-
- nrf_gpio_cfg_output(LED_3); //配置P0.19为输出,驱动led3
- nrf_gpio_cfg_output(LED_4);//配置P0.20为输出,驱动led4
- nrf_gpio_pin_set(LED_3);//led3初始状态设置为熄灭
- nrf_gpio_pin_set(LED_4);//led4初始状态设置为熄灭
- nrf_gpio_cfg_input(BUTTON_3,NRF_GPIO_PIN_PULLUP);//配置P0.15为输入
- nrf_gpio_cfg_input(BUTTON_4,NRF_GPIO_PIN_PULLUP);//配置P0.16为输入
-
- ret_code_t err_code;
- gpiote_init();
- my_ppi_init();
-
-
- while(true)
- {
- //检测按键3是否按下
- if(nrf_gpio_pin_read(BUTTON_3) == 0)
- {
- //等待按键释放
- while(nrf_gpio_pin_read(BUTTON_3) == 0);
- //使能GROUP
- err_code = nrfx_ppi_group_enable(my_ppi_group);
- //LED2亮LED3灭指示状态
- nrf_gpio_pin_set(BSP_LED_2);
- nrf_gpio_pin_clear(BSP_LED_3);
-
- }
- //检测按键4是否按下
- if(nrf_gpio_pin_read(BUTTON_4) == 0)
- {
- //等待按键释放
- while(nrf_gpio_pin_read(BUTTON_4) == 0);
- //使能GROUP
- err_code = nrfx_ppi_group_disable(my_ppi_group);
- //LED3亮LED2灭指示状态
- nrf_gpio_pin_set(BSP_LED_3);
- nrf_gpio_pin_clear(BSP_LED_2);
- }
-
- };
-
-
- }
-
- void gpiote_init(void)
- {
- ret_code_t err_code;
- //初始化GPIOTE
- err_code = nrf_drv_gpiote_init();
- APP_ERROR_CHECK(err_code);
-
- //配置LED端口翻转输出任务 按键1---LED0
- nrf_drv_gpiote_out_config_t out_config = GPIOTE_CONFIG_OUT_TASK_TOGGLE(true);
- //绑定输出端口
- err_code = nrf_drv_gpiote_out_init(BSP_LED_0,&out_config);
- //配置为输出端口任务使能
- nrf_drv_gpiote_out_task_enable(BSP_LED_0);
-
- //配置按键端口高电平变低电平事件
- nrf_drv_gpiote_in_config_t in_config = GPIOTE_CONFIG_IN_SENSE_HITOLO(true);
- in_config.pull = NRF_GPIO_PIN_PULLUP;
- //绑定输入端口
- err_code = nrf_drv_gpiote_in_init(BUTTON_1,&in_config,NULL);
- APP_ERROR_CHECK(err_code);
- //配置输入事件使能
- nrf_drv_gpiote_in_event_enable(BUTTON_1,true);
-
-
- //配置LED端口翻转输出任务 按键2---LED1
- nrf_drv_gpiote_out_config_t out_config_1 = GPIOTE_CONFIG_OUT_TASK_TOGGLE(true);
- //绑定输出端口
- err_code = nrf_drv_gpiote_out_init(BSP_LED_1,&out_config_1);
- //配置为输出端口任务使能
- nrf_drv_gpiote_out_task_enable(BSP_LED_1);
-
- //配置按键端口高电平变低电平事件
- nrf_drv_gpiote_in_config_t in_config_1 = GPIOTE_CONFIG_IN_SENSE_HITOLO(true);
- in_config_1.pull = NRF_GPIO_PIN_PULLUP;
- //绑定输入端口
- err_code = nrf_drv_gpiote_in_init(BUTTON_2,&in_config_1,NULL);
- APP_ERROR_CHECK(err_code);
- //配置输入事件使能
- nrf_drv_gpiote_in_event_enable(BUTTON_2,true);
-
- }
- void my_ppi_init(void)
- {
-
- ret_code_t err_code;
- //APP_ERROR_CHECK(err_code); //此处开启会运行不了!!!!!!!!!!!!!!!!!
-
-
- //初始化PPI模块
- err_code = nrf_drv_ppi_init();
- APP_ERROR_CHECK(err_code);
-
-
- //配置PPI的频道 PPI通道1
- err_code = nrfx_ppi_channel_alloc(&my_ppi_channel);
- APP_ERROR_CHECK(err_code);
-
- //设置PPI通道的EPP(事件)和TEP(任务) 两端对应的端口
- err_code = nrfx_ppi_channel_assign(my_ppi_channel,
- nrfx_gpiote_in_event_addr_get(BUTTON_1),
- nrfx_gpiote_out_task_addr_get(BSP_LED_0));
- APP_ERROR_CHECK(err_code);
-
-
- /*************************/
-
- //配置PPI的频道 PPI通道2
- err_code = nrfx_ppi_channel_alloc(&my_ppi_channel_1);
- APP_ERROR_CHECK(err_code);
-
- //设置PPI通道的EPP(事件)和TEP(任务) 两端对应的端口
- err_code = nrfx_ppi_channel_assign(my_ppi_channel_1,
- nrfx_gpiote_in_event_addr_get(BUTTON_2),
- nrfx_gpiote_out_task_addr_get(BSP_LED_1));
- APP_ERROR_CHECK(err_code);
-
-
-
- /*************************/
-
- //申请一个PPI组
- err_code = nrfx_ppi_group_alloc(&my_ppi_group);
- APP_ERROR_CHECK(err_code);
-
- //将PPI通道1加入PPI组
- err_code = nrfx_ppi_channel_include_in_group(my_ppi_channel,my_ppi_group);
- APP_ERROR_CHECK(err_code);
-
- //将PPI通道2加入PPI组
- err_code = nrfx_ppi_channel_include_in_group(my_ppi_channel_1,my_ppi_group);
- APP_ERROR_CHECK(err_code);
-
- }
eg:按下按键同时翻转LED1、2
寄存器
- void gpiote_init(void);
- void my_ppi_init(void);
- int main(void)
- {
-
- nrf_gpio_cfg_output(LED_3); //配置P0.19为输出,驱动led3
- nrf_gpio_cfg_output(LED_4);//配置P0.20为输出,驱动led4
- nrf_gpio_pin_set(LED_3);//led3初始状态设置为熄灭
- nrf_gpio_pin_set(LED_4);//led4初始状态设置为熄灭
- nrf_gpio_cfg_input(BUTTON_3,NRF_GPIO_PIN_PULLUP);//配置P0.15为输入
- nrf_gpio_cfg_input(BUTTON_4,NRF_GPIO_PIN_PULLUP);//配置P0.16为输入
-
- ret_code_t err_code;
- gpiote_init();
- my_ppi_init();
-
-
- while(true)
- {
-
- };
-
-
- }
-
- void gpiote_init(void)
- {
- nrf_gpio_cfg_input(BUTTON_1,NRF_GPIO_PIN_PULLUP);
-
- //配置一个GPIOTE输入任务
- NRF_GPIOTE->CONFIG[0] =
- (GPIOTE_CONFIG_POLARITY_HiToLo << GPIOTE_CONFIG_POLARITY_Pos) //触发极性的设置
- |(BUTTON_1<<GPIOTE_CONFIG_PSEL_Pos) //配置事件输入
- |(GPIOTE_CONFIG_MODE_Event << GPIOTE_CONFIG_MODE_Pos); //设置为事件模式
-
- //配置一个主任务
- NRF_GPIOTE->CONFIG[1] =
- (GPIOTE_CONFIG_POLARITY_Toggle << GPIOTE_CONFIG_POLARITY_Pos)
- |(LED_1<<GPIOTE_CONFIG_PSEL_Pos)
- |(GPIOTE_CONFIG_MODE_Task << GPIOTE_CONFIG_MODE_Pos);
-
- //配置fork从任务
- NRF_GPIOTE->CONFIG[2] =
- (GPIOTE_CONFIG_POLARITY_Toggle << GPIOTE_CONFIG_POLARITY_Pos)
- |(LED_2<<GPIOTE_CONFIG_PSEL_Pos)
- |(GPIOTE_CONFIG_MODE_Task << GPIOTE_CONFIG_MODE_Pos);
-
- }
- void my_ppi_init(void)
- {
- NRF_PPI ->CH[0].EEP = (uint32_t)(&NRF_GPIOTE->EVENTS_IN[0]);//配置输入事件
- NRF_PPI ->CH[0].TEP = (uint32_t)(&NRF_GPIOTE->TASKS_OUT[1]);//配置输出主任务
- NRF_PPI ->FORK[0].TEP = (uint32_t)(&NRF_GPIOTE->TASKS_OUT[2]);//配置输出从任务
-
- //使能通道0
- NRF_PPI->CHEN = (PPI_CHEN_CH0_Enabled << PPI_CHEN_CH0_Pos);
- }
库函数
- void gpiote_init(void);
- void my_ppi_init(void);
- static nrf_ppi_channel_t my_ppi_channel;
- int main(void)
- {
-
- nrf_gpio_cfg_output(LED_3); //配置P0.19为输出,驱动led3
- nrf_gpio_cfg_output(LED_4);//配置P0.20为输出,驱动led4
- nrf_gpio_pin_set(LED_3);//led3初始状态设置为熄灭
- nrf_gpio_pin_set(LED_4);//led4初始状态设置为熄灭
- nrf_gpio_cfg_input(BUTTON_3,NRF_GPIO_PIN_PULLUP);//配置P0.15为输入
- nrf_gpio_cfg_input(BUTTON_4,NRF_GPIO_PIN_PULLUP);//配置P0.16为输入
-
- ret_code_t err_code;
- gpiote_init();
- my_ppi_init();
-
-
- while(true)
- {
-
- };
-
-
- }
-
- void gpiote_init(void)
- {
- ret_code_t err_code;
- //初始化GPIOTE
- err_code = nrf_drv_gpiote_init();
- APP_ERROR_CHECK(err_code);
-
- //配置LED端口翻转输出任务 led1
- nrf_drv_gpiote_out_config_t out_config = GPIOTE_CONFIG_OUT_TASK_TOGGLE(true);
- //绑定输出端口
- err_code = nrf_drv_gpiote_out_init(BSP_LED_0,&out_config);
- //配置为输出端口任务使能
- nrf_drv_gpiote_out_task_enable(BSP_LED_0);
-
- //配置LED端口翻转输出任务 led2
- nrf_drv_gpiote_out_config_t out_config_1 = GPIOTE_CONFIG_OUT_TASK_TOGGLE(true);
- //绑定输出端口
- err_code = nrf_drv_gpiote_out_init(BSP_LED_1,&out_config_1);
- //配置为输出端口任务使能
- nrf_drv_gpiote_out_task_enable(BSP_LED_1);
-
-
- //配置按键端口高电平变低电平事件
- nrf_drv_gpiote_in_config_t in_config = GPIOTE_CONFIG_IN_SENSE_HITOLO(true);
- in_config.pull = NRF_GPIO_PIN_PULLUP;
- //绑定输入端口
- err_code = nrf_drv_gpiote_in_init(BUTTON_1,&in_config,NULL);
- APP_ERROR_CHECK(err_code);
- //配置输入事件使能
- nrf_drv_gpiote_in_event_enable(BUTTON_1,true);
-
-
- }
- void my_ppi_init(void)
- {
- ret_code_t err_code;
-
- //初始化PPI模块
- err_code = nrf_drv_ppi_init();
- APP_ERROR_CHECK(err_code);
-
- //配置PPI的频道
- err_code = nrfx_ppi_channel_alloc(&my_ppi_channel);
- APP_ERROR_CHECK(err_code);
-
- //设置PPI通道的EPP(事件)和TEP(任务) 两端对应的端口
- err_code = nrfx_ppi_channel_assign(my_ppi_channel,
- nrfx_gpiote_in_event_addr_get(BUTTON_1),
- nrfx_gpiote_out_task_addr_get(BSP_LED_0));
- APP_ERROR_CHECK(err_code);
- //PPI-fork
- err_code = nrfx_ppi_channel_fork_assign(my_ppi_channel,
- nrfx_gpiote_out_task_addr_get(BSP_LED_1));
-
- //使能PPI通道
- err_code = nrfx_ppi_channel_enable(my_ppi_channel);
- APP_ERROR_CHECK(err_code);
- }
通过PPI通道,让定时器1定时1秒后开启定时器0的计数,定时器2定时2秒关闭定时器0的计数。
寄存器
- void timer0_init(void)
- {
- //定时器0配置为计数模式
- NRF_TIMER0->MODE = TIMER_MODE_MODE_Counter;
- //设置定时器的分频
- NRF_TIMER0->PRESCALER = 9;
- //定时器位宽
- NRF_TIMER0->BITMODE = TIMER_BITMODE_BITMODE_16Bit;
- }
- void timer1_init(void)
- {
- //定时器1配置为定时模式,定时2秒(第2秒停止启动同时开始此时停止优先)
-
- //位宽 BITMODE = 16bit
- NRF_TIMER1->BITMODE = (TIMER_BITMODE_BITMODE_16Bit << TIMER_BITMODE_BITMODE_Pos);
- //定时器分频值 PRESCALER = 9
- NRF_TIMER1 ->PRESCALER = 9;
- //定时器比较清零计数器模式实现不停的定时,本地任务和事件的快捷方式
- NRF_TIMER1->SHORTS = (TIMER_SHORTS_COMPARE0_CLEAR_Enabled << TIMER_SHORTS_COMPARE1_CLEAR_Pos);
- //定时器模式
- NRF_TIMER1->MODE = TIMER_MODE_MODE_Timer;
- //触发时间 = 0XFFFF/(SysClk/2^PERSCALER) = 65535/31250 = 2.097 sec
- NRF_TIMER1 ->CC[0] = 0xFFFFUL;
- }
- void timer2_init(void)
- {
- //定时器2配置为定时模式,定时1秒
-
- //位宽 BITMODE = 16bit
- NRF_TIMER2->BITMODE = (TIMER_BITMODE_BITMODE_16Bit << TIMER_BITMODE_BITMODE_Pos);
- //定时器分频值 PRESCALER = 9
- NRF_TIMER2 ->PRESCALER = 9;
- //定时器比较清零计数器模式实现不停的定时,本地任务和事件的快捷方式
- NRF_TIMER2->SHORTS = (TIMER_SHORTS_COMPARE0_CLEAR_Enabled << TIMER_SHORTS_COMPARE1_CLEAR_Pos);
- //定时器模式
- NRF_TIMER2->MODE = TIMER_MODE_MODE_Timer;
- //触发时间 = 0XFFFF/(SysClk/2^PERSCALER) = 32767/31250 = 1.048 sec 触发比较事件
- NRF_TIMER2 ->CC[0] = 0x7FFFUL;
- }
- void ppi_init(void)
- {
- //配置PPI通道0,事件端为定时器1的比较定时事件,任务端为关闭定时器0;
- NRF_PPI->CH[0].EEP = (uint32_t)(&NRF_TIMER1->EVENTS_COMPARE[0]);
- NRF_PPI->CH[0].TEP = (uint32_t)(&NRF_TIMER0->TASKS_STOP);
- //配置PPI通道1,事件端为定时器2的比较事件,任务端为开启定时器0;
- NRF_PPI->CH[1].EEP = (uint32_t)(&NRF_TIMER2->EVENTS_COMPARE[0]);
- NRF_PPI->CH[1].TEP = (uint32_t)(&NRF_TIMER0->TASKS_START);
- //使能PPI通道0、1
- NRF_PPI->CHEN = (PPI_CHEN_CH0_Enabled<< PPI_CHEN_CH0_Pos) |(PPI_CHEN_CH1_Enabled << PPI_CHEN_CH1_Pos);
- }
-
- /**
- * @brief Function for main application entry.
- */
- int main(void)
- {
-
- uint32_t err_code;
-
- bsp_board_init(BSP_INIT_LEDS);
-
- const app_uart_comm_params_t comm_params =
- {
- RX_PIN_NUMBER,
- TX_PIN_NUMBER,
- RTS_PIN_NUMBER,
- CTS_PIN_NUMBER,
- UART_HWFC,
- false,
- #if defined (UART_PRESENT)
- NRF_UART_BAUDRATE_115200
- #else
- NRF_UARTE_BAUDRATE_115200
- #endif
- };
-
- APP_UART_FIFO_INIT(&comm_params,
- UART_RX_BUF_SIZE,
- UART_TX_BUF_SIZE,
- uart_error_handle,
- APP_IRQ_PRIORITY_LOWEST,
- err_code);
-
- APP_ERROR_CHECK(err_code);
-
- #ifndef ENABLE_LOOPBACK_TEST
-
- int32_t timval;
- timer0_init();
- timer1_init();
- timer2_init();
- ppi_init();
-
- //启动定时器
- NRF_TIMER1 ->TASKS_START = 1;
- NRF_TIMER2 ->TASKS_START = 1;
- while (true)
- {
- //计数器加1
- NRF_TIMER0->TASKS_COUNT = 1;
- //捕获输出
- NRF_TIMER0->TASKS_CAPTURE[0] =1;
- //获取计数值
- timval = NRF_TIMER0->CC[0];
- printf("count value:%d\r\n",timval);
- nrf_delay_ms(1048);
- }
- #else
-
- // This part of the example is just for testing the loopback .
- while (true)
- {
- uart_loopback_test();
- }
- #endif
-
- }
库函数
- const nrf_drv_timer_t timer0 = NRF_DRV_TIMER_INSTANCE(0);
- const nrf_drv_timer_t timer1 = NRF_DRV_TIMER_INSTANCE(1);
- const nrf_drv_timer_t timer2 = NRF_DRV_TIMER_INSTANCE(2);
-
- nrf_ppi_channel_t my_ppi_channel=NRF_PPI_CHANNEL0 ;
- nrf_ppi_channel_t my_ppi_channel2=NRF_PPI_CHANNEL1;
-
- void timer0_init(void)
- {
- uint32_t time_ticks;
- ret_code_t err_code;
- nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
- timer_cfg.mode = NRF_TIMER_MODE_COUNTER;
- err_code = nrf_drv_timer_init(&timer0,&timer_cfg,NULL);
- APP_ERROR_CHECK(err_code);
- }
- void timer1_init(void)
- {
- uint32_t time_ticks;
- ret_code_t err_code;
- nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
- err_code = nrf_drv_timer_init(&timer1,&timer_cfg,NULL);
- APP_ERROR_CHECK(err_code);
- nrf_drv_timer_extended_compare(&timer1,NRF_TIMER_CC_CHANNEL0,0xFFFFUL,NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK,false);
- nrf_drv_timer_enable(&timer1);
-
- }
- void timer2_init(void)
- {
- uint32_t time_ticks;
- ret_code_t err_code;
- nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
- err_code = nrf_drv_timer_init(&timer2,&timer_cfg,NULL);
- APP_ERROR_CHECK(err_code);
- nrf_drv_timer_extended_compare(&timer2,NRF_TIMER_CC_CHANNEL0,0x7FFFUL,NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK,false);
- nrf_drv_timer_enable(&timer2);
- }
- void ppi_init(void)
- {
- ret_code_t err_code;
- err_code = nrf_drv_ppi_init();
- APP_ERROR_CHECK(err_code);
-
- err_code = nrf_drv_ppi_channel_alloc(&my_ppi_channel);
- APP_ERROR_CHECK(err_code);
- err_code = nrf_drv_ppi_channel_assign(my_ppi_channel,nrf_drv_timer_event_address_get(&timer2,NRF_TIMER_EVENT_COMPARE0)
- ,nrf_drv_timer_task_address_get(&timer0,NRF_TIMER_TASK_START));
- APP_ERROR_CHECK(err_code);
- err_code = nrf_drv_ppi_channel_enable(my_ppi_channel);
- APP_ERROR_CHECK(err_code);
-
- err_code = nrf_drv_ppi_channel_alloc(&my_ppi_channel2);
- APP_ERROR_CHECK(err_code);
- err_code = nrf_drv_ppi_channel_assign(my_ppi_channel2,nrf_drv_timer_event_address_get(&timer1,NRF_TIMER_EVENT_COMPARE0)
- ,nrf_drv_timer_task_address_get(&timer0,NRF_TIMER_TASK_STOP));
- APP_ERROR_CHECK(err_code);
- err_code = nrf_drv_ppi_channel_enable(my_ppi_channel2);
- APP_ERROR_CHECK(err_code);
- }
-
- /**
- * @brief Function for main application entry.
- */
- int main(void)
- {
-
- uint32_t err_code;
-
- bsp_board_init(BSP_INIT_LEDS);
-
- const app_uart_comm_params_t comm_params =
- {
- RX_PIN_NUMBER,
- TX_PIN_NUMBER,
- RTS_PIN_NUMBER,
- CTS_PIN_NUMBER,
- UART_HWFC,
- false,
- #if defined (UART_PRESENT)
- NRF_UART_BAUDRATE_115200
- #else
- NRF_UARTE_BAUDRATE_115200
- #endif
- };
-
- APP_UART_FIFO_INIT(&comm_params,
- UART_RX_BUF_SIZE,
- UART_TX_BUF_SIZE,
- uart_error_handle,
- APP_IRQ_PRIORITY_LOWEST,
- err_code);
-
- APP_ERROR_CHECK(err_code);
-
- #ifndef ENABLE_LOOPBACK_TEST
-
- int32_t timval;
- timer0_init();
- timer1_init();
- timer2_init();
- ppi_init();
- //启动定时器0
- nrf_drv_timer_enable(&timer0);
- while (true)
- {
- //计数器加1
- nrfx_timer_increment(&timer0);
- //获取计数值
- timval = nrfx_timer_capture(&timer0,NRF_TIMER_CC_CHANNEL0);
- printf("count value:%d\r\n",timval);
- nrf_delay_ms(1048);
- }
- #else
-
- // This part of the example is just for testing the loopback .
- while (true)
- {
- uart_loopback_test();
- }
- #endif
-
- }
实现方法通过定时器触发GPIOTE的输出电平变化来实现,定时器作为任务,触发的GPIOTE作为事件。
eg:使用一个定时器通过不同的CC[],编写两路PWM输出,需要4路PPI通道。每两路PPI通道产生一个PWM输出,其中一路作为占空比的输出控制,另外一路作为PWM周期的控制整个过程CPU不参与其中。
① eg:固定周期不同占空比的两路PWM
寄存器
- /**
- * @brief Function for application main entry.
- */
- void timer0_init(void)
- {
-
- NRF_TIMER0 ->PRESCALER = 4; //2^4 16分频成1M时钟源
- NRF_TIMER0 ->MODE = 0; //time模式
- NRF_TIMER0 ->BITMODE = 3; //32bit
-
- NRF_TIMER0->CC[1] = 5000; //cc[1]的值等于5ms,这里相当于方波的周期为5ms
- NRF_TIMER0->CC[0] = 100; //cc[0]为占空比值
-
- NRF_TIMER0->CC[2] = 5000; //cc[2]的值等于5ms,这里相当于方波的周期为5ms
- NRF_TIMER0->CC[3] = 4900; //cc[3]为占空比值
-
- NRF_TIMER0->SHORTS = 1<<1; //设置倒计时到CC1中的值时自动清零重新开始计数;
- NRF_TIMER0->SHORTS = 1<<2; //设置倒计时到CC2中的值时自动清零重新开始计数;
-
- NRF_TIMER0->TASKS_START = 1; //开启timer
-
- }
- //电平不停翻转,配置GPIOTE 0 与1
- void gpoite_init(void)
- {
- NRF_GPIOTE->CONFIG[0] = ( 3 << 0 ) //作为task模式
- | ( BSP_LED_0 << 8) //设置PWM输出引脚
- | ( 3 << 16 ) //设置task为翻转PWM引脚的电平
- | ( 1 << 20); //初始输出电平为高
- NRF_GPIOTE->CONFIG[1] = ( 3 << 0 ) //作为task模式
- | ( BSP_LED_1 << 8) //设置PWM输出引脚
- | ( 3 << 16 ) //设置task为翻转PWM引脚的电平
- | ( 1 << 20); //初始输出电平为高
- }
- void ppi_set(void)
- {
-
- //配置每个PPI对应的事件和任务
- NRF_PPI->CH[0].EEP = (uint32_t)(&NRF_TIMER0->EVENTS_COMPARE[0]);
- NRF_PPI->CH[0].TEP = (uint32_t)(&NRF_GPIOTE->TASKS_OUT[0]);
-
- NRF_PPI->CH[1].EEP = (uint32_t)(&NRF_TIMER0->EVENTS_COMPARE[1]);
- NRF_PPI->CH[1].TEP = (uint32_t)(&NRF_GPIOTE->TASKS_OUT[0]);
-
- NRF_PPI->CH[2].EEP = (uint32_t)(&NRF_TIMER0->EVENTS_COMPARE[2]);
- NRF_PPI->CH[2].TEP = (uint32_t)(&NRF_GPIOTE->TASKS_OUT[1]);
-
- NRF_PPI->CH[3].EEP = (uint32_t)(&NRF_TIMER0->EVENTS_COMPARE[3]);
- NRF_PPI->CH[3].TEP = (uint32_t)(&NRF_GPIOTE->TASKS_OUT[1]);
-
- //任务绑定
- //两个通道的task端绑定的都是翻转电平的task
- //使能PPI通道 0 和 通道1 1111
- NRF_PPI->CHENSET = 0xf;
- }
-
- int main(void)
- {
- timer0_init();
- gpoite_init();
- ppi_set();
-
- /* Toggle LEDs. */
- while (true)
- {
-
- }
- }
现象 LED1与LED2的亮度不一样
② eg:变占空比动态调节LED亮度\
- APP_PWM_INSTANCE(PWM1,1); // 创建一个使用定时器1产生PWM波的实例
- static volatile bool ready_flag; // 使用一个标志位表示PWM状态
-
- void pwm_ready_callback(uint32_t pwm_id) // PWM callback function
- {
- ready_flag = true;
- }
-
- int main(void)
- {
- ret_code_t err_code;
-
- //配置2个通道的PWM ,200Hz(50000us = 5ms),通过LED0、1管脚输出
- app_pwm_config_t pwm1_cfg = APP_PWM_DEFAULT_CONFIG_2CH(5000L,BSP_LED_0,BSP_LED_1);
- //初始输出电平配置为高
- pwm1_cfg.pin_polarity[1] = APP_PWM_POLARITY_ACTIVE_HIGH;
- //初始和使能PWM
- err_code = app_pwm_init(&PWM1,&pwm1_cfg,pwm_ready_callback);
- APP_ERROR_CHECK(err_code);
- app_pwm_enable(&PWM1);
-
- uint32_t value;
- while (true)
- {
- for (uint8_t i = 0; i < 40; ++i)
- {
- value = (i < 20) ? (i * 5) : (100 - (i - 20) * 5);
-
- ready_flag = false;
- /* 设置占空比 - 不停设置直到PWM准备好. */
- while (app_pwm_channel_duty_set(&PWM1, 0, value) == NRF_ERROR_BUSY);
-
- /* 等待回调 */
- while (!ready_flag);
- APP_ERROR_CHECK(app_pwm_channel_duty_set(&PWM1, 1, value));
- nrf_delay_ms(25);
- }
- }
-
- }
#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
#endifSCK = 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
#endifMCK,主时钟,也叫作系统时钟,是采样频率的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,
官方提供了一个I2S的回环数据传输,下面分析是如何实现双缓冲的乒乓结构
加了分析注释的源代码
-
- #include <stdio.h>
- #include "nrf_drv_i2s.h"
- #include "nrf_delay.h"
- #include "app_util_platform.h"
- #include "app_error.h"
- #include "boards.h"
-
- #include "nrf_log.h"
- #include "nrf_log_ctrl.h"
- #include "nrf_log_default_backends.h"
-
- #define LED_OK BSP_BOARD_LED_0
- #define LED_ERROR BSP_BOARD_LED_1
-
- #define I2S_DATA_BLOCK_WORDS 512 //一个块的大小
- static uint32_t m_buffer_rx[2][I2S_DATA_BLOCK_WORDS];
- static uint32_t m_buffer_tx[2][I2S_DATA_BLOCK_WORDS];
-
- // Delay time between consecutive I2S transfers performed in the main loop
- // (in milliseconds).
- #define PAUSE_TIME 500
- // Number of blocks of data to be contained in each transfer.
- #define BLOCKS_TO_TRANSFER 20 //传多少个块
-
- static uint8_t volatile m_blocks_transferred = 0; //有多少个块被传输
- static uint8_t m_zero_samples_to_ignore = 0; //开始传输时会先传两个字节全0的数据流,并非我们要发送的数据
- static uint16_t m_sample_value_to_send;
- static uint16_t m_sample_value_expected;
- static bool m_error_encountered;
-
- static uint32_t * volatile mp_block_to_fill = NULL;
- static uint32_t const * volatile mp_block_to_check = NULL;
-
-
- static void prepare_tx_data(uint32_t * p_block)
- {
- // These variables will be both zero only at the very beginning of each
- // transfer, so we use them as the indication that the re-initialization
- // should be performed.
- if (m_blocks_transferred == 0 && m_zero_samples_to_ignore == 0)
- {
- // Number of initial samples (actually pairs of L/R samples) with zero
- // values that should be ignored - see the comment in 'check_samples'.
- m_zero_samples_to_ignore = 2;
- m_sample_value_to_send = 0xCAFE;
- m_sample_value_expected = 0xCAFE;
- m_error_encountered = false;
- }
-
- // [each data word contains two 16-bit samples]
- uint16_t i;
- for (i = 0; i < I2S_DATA_BLOCK_WORDS; ++i)
- {
- uint16_t sample_l = m_sample_value_to_send - 1;
- uint16_t sample_r = m_sample_value_to_send + 1;
- ++m_sample_value_to_send;
-
- uint32_t * p_word = &p_block[i];
- ((uint16_t *)p_word)[0] = sample_l;
- ((uint16_t *)p_word)[1] = sample_r;
- }
- }
-
-
- static bool check_samples(uint32_t const * p_block)
- {
- // [each data word contains two 16-bit samples]
- uint16_t i;
- for (i = 0; i < I2S_DATA_BLOCK_WORDS; ++i)
- {
- uint32_t const * p_word = &p_block[i];
- uint16_t actual_sample_l = ((uint16_t const *)p_word)[0];
- uint16_t actual_sample_r = ((uint16_t const *)p_word)[1];
-
- // Normally a couple of initial samples sent by the I2S peripheral
- // will have zero values, because it starts to output the clock
- // before the actual data is fetched by EasyDMA. As we are dealing
- // with streaming the initial zero samples can be simply ignored.
- if (m_zero_samples_to_ignore > 0 &&
- actual_sample_l == 0 &&
- actual_sample_r == 0)
- {
- --m_zero_samples_to_ignore;
- }
- else
- {
- m_zero_samples_to_ignore = 0;
-
- uint16_t expected_sample_l = m_sample_value_expected - 1;
- uint16_t expected_sample_r = m_sample_value_expected + 1;
- ++m_sample_value_expected;
-
- if (actual_sample_l != expected_sample_l ||
- actual_sample_r != expected_sample_r)
- {
- NRF_LOG_INFO("%3u: %04x/%04x, expected: %04x/%04x (i: %u)",
- m_blocks_transferred, actual_sample_l, actual_sample_r,
- expected_sample_l, expected_sample_r, i);
- return false;
- }
- }
- }
- NRF_LOG_INFO("%3u: OK", m_blocks_transferred);
- return true;
- }
-
-
- static void check_rx_data(uint32_t const * p_block)
- {
- ++m_blocks_transferred;
-
- if (!m_error_encountered)
- {
- m_error_encountered = !check_samples(p_block);
- }
-
- if (m_error_encountered)
- {
- bsp_board_led_off(LED_OK);
- bsp_board_led_invert(LED_ERROR);
- }
- else
- {
- bsp_board_led_off(LED_ERROR);
- bsp_board_led_invert(LED_OK);
- }
- }
-
-
- static void data_handler(nrf_drv_i2s_buffers_t const * p_released,
- uint32_t status)
- {
- // 'nrf_drv_i2s_next_buffers_set' is called directly from the handler
- // each time next buffers are requested, so data corruption is not
- // expected.
- ASSERT(p_released);
-
- // When the handler is called after the transfer has been stopped
- // (no next buffers are needed, only the used buffers are to be
- // released), there is nothing to do.
- if (!(status & NRFX_I2S_STATUS_NEXT_BUFFERS_NEEDED))
- {
- NRF_LOG_INFO("----STOP-----");
- return;
- }
- //p_released 指向结构的指针,该结构指向以前传递给驱动程序的缓冲区的指针,
- //该驱动程序将不再被它访问(现在可以安全地释放它们或将其用于其他目的,特别是用于传输的下一部分)
-
- // First call of this handler occurs right after the transfer is started.
- // No data has been transferred yet at this point, so there is nothing to
- // check. Only the buffers for the next part of the transfer should be
- // provided.
-
- if (!p_released->p_rx_buffer)
- {
- //在开始传输后首次调用处理程序时,此结构中的两个指针均为 NULL,因为此时尚未传输任何数据。
- //此时p_released = NULL,下一个buff指向m_buffer_tx[1]。
- NRF_LOG_INFO("----DONE-----");
- nrf_drv_i2s_buffers_t const next_buffers = {
- .p_rx_buffer = m_buffer_rx[1],
- .p_tx_buffer = m_buffer_tx[1],
- };
- //指定下一个buff
- APP_ERROR_CHECK(nrf_drv_i2s_next_buffers_set(&next_buffers));
-
- mp_block_to_fill = m_buffer_tx[1];
- }
- else
- {
- if(p_released->p_tx_buffer == m_buffer_tx[0])
- {
- NRF_LOG_INFO("-----m_buffer_tx[0]-----");
- }
- else if(p_released->p_tx_buffer == m_buffer_tx[1])
- {
- NRF_LOG_INFO("-----m_buffer_tx[1]-----");
- }
-
- //在所有连续调用中,指针指定刚刚完成的传输部分中已发送的内容 (TX) 和已接收的内容 (RX)
- NRF_LOG_INFO("----NEXT-----");
- mp_block_to_check = p_released->p_rx_buffer;
- // The driver has just finished accessing the buffers pointed by
- // 'p_released'. They can be used for the next part of the transfer
- // that will be scheduled now.
- //此时m_buffer_tx[0] 传输完毕,p_released指向的是m_buffer_tx[0],下一个buff指向m_buffer_tx[0]。
- APP_ERROR_CHECK(nrf_drv_i2s_next_buffers_set(p_released));
-
- // The pointer needs to be typecasted here, so that it is possible to
- // modify the content it is pointing to (it is marked in the structure
- // as pointing to constant data because the driver is not supposed to
- // modify the provided data).
- mp_block_to_fill = (uint32_t *)p_released->p_tx_buffer;
-
-
- }
- }
-
-
- void app_error_fault_handler(uint32_t id, uint32_t pc, uint32_t info)
- {
- bsp_board_leds_on();
- app_error_save_and_stop(id, pc, info);
- }
-
-
- int main(void)
- {
- uint32_t err_code = NRF_SUCCESS;
-
- bsp_board_init(BSP_INIT_LEDS);
-
- err_code = NRF_LOG_INIT(NULL);
- APP_ERROR_CHECK(err_code);
-
- NRF_LOG_DEFAULT_BACKENDS_INIT();
-
- NRF_LOG_INFO("#########################1");
-
- 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_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);
- APP_ERROR_CHECK(err_code);
- for (;;)
- {
- NRF_LOG_INFO("#########################2");
- m_blocks_transferred = 0;
- mp_block_to_fill = NULL;
- mp_block_to_check = NULL;
- prepare_tx_data(m_buffer_tx[0]);
- nrf_drv_i2s_buffers_t const initial_buffers = {
- .p_tx_buffer = m_buffer_tx[0],
- .p_rx_buffer = m_buffer_rx[0],
- };
- err_code = nrf_drv_i2s_start(&initial_buffers, I2S_DATA_BLOCK_WORDS, 0);
- APP_ERROR_CHECK(err_code);
- do {
- // Wait for an event.
- __WFE();
- // Clear the event register.
- __SEV();
- __WFE();
- if (mp_block_to_fill)
- {
- prepare_tx_data(mp_block_to_fill);
- mp_block_to_fill = NULL;
- }
- if (mp_block_to_check)
- {
- check_rx_data(mp_block_to_check);
- mp_block_to_check = NULL;
- }
- } while (m_blocks_transferred < BLOCKS_TO_TRANSFER);
- nrf_drv_i2s_stop();
- NRF_LOG_FLUSH();
- bsp_board_leds_off();
- nrf_delay_ms(PAUSE_TIME);
- }
- }
- /** @} */
输出结果
分析
nRF5 SDK v15.3.0: I2S Loopback Example
nRF52832 — 使用nRF52832的I2S播放音频_52832 蓝牙语音_文化人Sugar的博客-CSDN博客
【通信协议】I2S/IIS总线介绍_iis接口_努力努力再努力~~的博客-CSDN博客
I2S的理解_i2s通信的详细讲解_wholetus的博客-CSDN博客
三角波,正弦波,方波数据通过I2S传输到MAX98357生成对应波形。
- #define PIN_MCK (13)
- #define PIN_SCK (14)
- #define PIN_LRCK (15)
- #define PIN_SDOUT (16)
- void sin_text();
-
- int main(void)
- {
-
- sin_text();
-
- }
-
- void sin_text()
- {
- // Sine table (stereo, so every value is duplicated)
- int16_t sine_table[] = {
- 2047,2447,2831,3185,3498,3750,3939,
- 4056,4095,4056,3939,3758,3495,3185,
- 2831,2447,2047,1647,1263,909,599,344,
- 155,38,0,38,155,344,599,909,1263,1647};
-
- int16_t triangle_table[] = {
- 0,256,512,768,1024,1279,1535,1791,
- 2047,2303,2559,2815,3071,3326,3582,3838,
- 4095,3838,3582,3326,3071,2815,2559,2303,
- 2047,1791,1535,1279,1024,768,512,256};
-
- int16_t square_table[] = {
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 4095,4095,4095,4095,4095,4095,4095,4095,4095,4095,4095,4095,4095,4095,4095,4095,4095,4095,4095,4095,
- 4095,4095,4095,4095,4095,4095,4095,4095,4095,4095,4095,4095,4095,4095,4095,4095,4095,4095,4095,4095,};
-
-
- // Enable transmission
- NRF_I2S->CONFIG.TXEN = (I2S_CONFIG_TXEN_TXEN_ENABLE << I2S_CONFIG_TXEN_TXEN_Pos);
-
- // Enable MCK generator
- NRF_I2S->CONFIG.MCKEN = (I2S_CONFIG_MCKEN_MCKEN_ENABLE << I2S_CONFIG_MCKEN_MCKEN_Pos);
-
- // MCKFREQ = 4 MHz
- NRF_I2S->CONFIG.MCKFREQ = I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV11 << I2S_CONFIG_MCKFREQ_MCKFREQ_Pos;
-
- // Ratio = 64
- NRF_I2S->CONFIG.RATIO = I2S_CONFIG_RATIO_RATIO_64X << I2S_CONFIG_RATIO_RATIO_Pos;
-
- // Master mode, 16Bit, left aligned
- NRF_I2S->CONFIG.MODE = I2S_CONFIG_MODE_MODE_MASTER << I2S_CONFIG_MODE_MODE_Pos;
- NRF_I2S->CONFIG.SWIDTH = I2S_CONFIG_SWIDTH_SWIDTH_16BIT << I2S_CONFIG_SWIDTH_SWIDTH_Pos;
- NRF_I2S->CONFIG.ALIGN = I2S_CONFIG_ALIGN_ALIGN_LEFT << I2S_CONFIG_ALIGN_ALIGN_Pos;
-
- // Format = I2S
- NRF_I2S->CONFIG.FORMAT = I2S_CONFIG_FORMAT_FORMAT_I2S << I2S_CONFIG_FORMAT_FORMAT_Pos;
-
- // Use stereo
- NRF_I2S->CONFIG.CHANNELS = I2S_CONFIG_CHANNELS_CHANNELS_STEREO << I2S_CONFIG_CHANNELS_CHANNELS_Pos;
-
- // Configure pins
- NRF_I2S->PSEL.MCK = (PIN_MCK << I2S_PSEL_MCK_PIN_Pos);
- NRF_I2S->PSEL.SCK = (PIN_SCK << I2S_PSEL_SCK_PIN_Pos);
- NRF_I2S->PSEL.LRCK = (PIN_LRCK << I2S_PSEL_LRCK_PIN_Pos);
- NRF_I2S->PSEL.SDOUT = (PIN_SDOUT << I2S_PSEL_SDOUT_PIN_Pos);
-
- NRF_I2S->ENABLE = 1;
-
- // Configure data pointer
- NRF_I2S->TXD.PTR = (uint32_t)&square_table[0];
- NRF_I2S->RXTXD.MAXCNT = sizeof(square_table) / sizeof(uint32_t);
-
- // Start transmitting I2S data
- NRF_I2S->TASKS_START = 1;
-
-
- // Since we are not updating the TXD pointer, the sine wave will play over and over again.
- // The TXD pointer can be updated after the EVENTS_TXPTRUPD arrives.
- while (1)
- {
- __WFE();
- }
-
- }
- D类功放输出的是包含音频信息的调制方波,而非直接的音频波形数据所以示波器无法直接看输出的波形。详见
- 所以我是通过音频采集器用AU来采集
- 方波
- 三角波
- 正弦波
相关文章
原始数据不能用const修饰,因为EDMA只能读取内存中的数据,不能读取FLASH中的
输中使用的uint32_t常量指针类型,不能使用的uint8_t常量指针类型
I2S模块为双缓冲,可以在写入电流的同时准备下一个缓冲器,每个缓冲器的最大大小为8192bytes
通过nRF52840 I2S传输正弦波,使用简单的DMA和MAX98357A编解码器 - 北欧问答 - 北欧开发区 - 北欧开发区 (nordicsemi.com)
这个折腾了2天参照例程和资料
- 截取了一段1S钟16位44.1Khz的WAV音频数据;
- 用的I2S的双缓冲,这里我设置为10K的缓冲太小了会卡顿。
- #include "wav.h" //wav数组
-
-
-
-
- #define I2S_DATA_BLOCK_WORDS 10*1024 //buff大小
- static uint32_t m_buffer_tx[2][I2S_DATA_BLOCK_WORDS];
-
-
- static uint32_t * volatile mp_block_to_fill = NULL;
-
- uint32_t length;
-
- static void prepare_tx_data(uint32_t * p_block)
- {
- memcpy(p_block,wave_data+length,I2S_DATA_BLOCK_WORDS);
- length +=I2S_DATA_BLOCK_WORDS;
- if(length >= 39396) length = 0;
- }
-
- static void data_handler(nrf_drv_i2s_buffers_t const * p_released,
- uint32_t status)
- {
-
- ASSERT(p_released);
-
-
- if (!(status & NRFX_I2S_STATUS_NEXT_BUFFERS_NEEDED))
- {
- return;
- }
-
-
- if (!p_released->p_tx_buffer)
- {
- nrf_drv_i2s_buffers_t const next_buffers = {
- .p_tx_buffer = m_buffer_tx[1],
- };
- APP_ERROR_CHECK(nrf_drv_i2s_next_buffers_set(&next_buffers));
- mp_block_to_fill = m_buffer_tx[1];
- }
- else
- {
- APP_ERROR_CHECK(nrf_drv_i2s_next_buffers_set(p_released));
- mp_block_to_fill = (uint32_t *)p_released->p_tx_buffer;
-
- }
- }
-
- void app_error_fault_handler(uint32_t id, uint32_t pc, uint32_t info)
- {
- bsp_board_leds_on();
- app_error_save_and_stop(id, pc, info);
- }
- void sin_text();
-
-
- #define PIN_MCK (13)
- #define PIN_SCK (14)
- #define PIN_LRCK (15)
- #define PIN_SDOUT (16)
- #define I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV3 (0x50000000UL)
-
- int main(void)
- {
-
- #if 1
-
- uint32_t err_code = NRF_SUCCESS;
- bsp_board_init(BSP_INIT_LEDS);
-
- err_code = NRF_LOG_INIT(NULL);
- APP_ERROR_CHECK(err_code);
-
- NRF_LOG_DEFAULT_BACKENDS_INIT();
-
-
- nrf_drv_i2s_config_t config = NRF_DRV_I2S_DEFAULT_CONFIG;
- config.sck_pin = PIN_SCK;
- config.lrck_pin = PIN_LRCK;
- config.sdout_pin = PIN_SDOUT;
- config.mode = NRF_I2S_MODE_MASTER;
- config.format = NRF_I2S_FORMAT_I2S;
- config.alignment = NRF_I2S_ALIGN_LEFT;
- config.sample_width = NRF_I2S_SWIDTH_16BIT;
- config.channels = NRF_I2S_CHANNELS_LEFT;
- config.mck_setup = I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV3;
- config.ratio = NRF_I2S_RATIO_256X;
-
- err_code = nrf_drv_i2s_init(&config, data_handler);
- APP_ERROR_CHECK(err_code);
-
-
- mp_block_to_fill = NULL;
- prepare_tx_data(m_buffer_tx[0]);
-
- nrf_drv_i2s_buffers_t const initial_buffers = {.p_tx_buffer = m_buffer_tx[0]};
- err_code = nrf_drv_i2s_start(&initial_buffers, I2S_DATA_BLOCK_WORDS, 0);
- APP_ERROR_CHECK(err_code);
- for (;;)
- {
- // Wait for an event.
- __WFE();
- // Clear the event register.
- __SEV();
- __WFE();
-
- if (mp_block_to_fill)
- {
- prepare_tx_data(mp_block_to_fill);
- mp_block_to_fill = NULL;
- }
- }
- #endif
- }
原始音频波形
生成的
①:采样模式
②:信号增益
③:参考电压
④:采样精度
⑤:工作模式
结合PPI,双Buff,EDMA来运行;
实战经验,Nordic 52832 低功耗模式与唤醒机制 (360doc.com)
STM32Cube MX USB双设备MSC+CDC 实现虚拟U盘+虚拟串口_stm32 usb虚拟串口和u盘_Pzkkkkk的博客-CSDN博客
- #include "dht11.h"
- #include "nrf_delay.h"
-
- /*
- *开始信号
- *开始信号输出后从机会回应一个应答信号
- */
- void dht_Rst(void)
- {
- dht_IO_OUT(); //IO口配置为输出
- dht_data_OUT_0; //输出0
- nrf_delay_ms(25); //输出25ms
- dht_data_OUT_1; //输出1
- nrf_delay_us(30); //输出30us
- }
- /*
- *检测应答信号
- *正确应答返回0错误应答返回1
- */
- uint8_t dht_Check(void)
- {
- uint8_t time=0;
- dht_IO_IN(); //输入模式
- while (dht_data_IN&&time<100) //输入电平为低且时机小于100us为应答信号
- {
- time++;
- nrf_delay_us(1);
- }
- if(time>=100) //时间大于100us应答错误
- {
- printf("error\r\n");
- return 1;
- }
- else time=0;
- while (!dht_data_IN&&time<100)//输入电平为高且时机小于100us为数据开始传输
- {
- time++;
- nrf_delay_us(1);
-
- }
- if(time>=100)
- {
- printf("error 2\r\n");
- return 1;
- }
- return 0; //数据开始传输
- }
- /*
- *读取一个bit的数据
- */
- uint8_t dht_Read_Bit(void)
- {
- uint8_t time=0;
- while(dht_data_IN&&time<100) //数据输的开始位为低电平(50us)
- {
- time++;
- nrf_delay_us(1);
- }
- time=0;
- while(!dht_data_IN&&time<100) //开始读取高电平时间,判断数据为1还是0
- {
- time++;
- nrf_delay_us(1);
- }
- nrf_delay_us(40); //延迟70毫秒后若还为高电平则为1否则为0
- if(dht_data_IN)return 1;
- else return 0;
- }
- /*
- *读取一个字节的数据
- */
- uint8_t dht_Read_Byte(void)
- {
- uint8_t i,dat;
- dat=0;
- for (i=0;i<8;i++)
- {
- dat<<=1;
- dat|=dht_Read_Bit();
- }
- return dat;
- }
- uint8_t dht_Read_Data(uint8_t *temp,uint8_t *humi)
- {
- uint8_t dat[5];
- uint8_t i;
- dht_Rst();
- if(dht_Check()==0)
- {
- for(i=0;i<5;i++)
- {
- dat[i]=dht_Read_Byte();
- }
- if((dat[0]+dat[1]+dat[2]+dat[3])==dat[4]) //校验
- {
- *humi=dat[0]; //温度整数位
- *temp=dat[2]; //湿度整数位
- }
- }else return 1;
- return 0;
- }
-
- uint8_t dht_Init(void)
- {
- dht_Rst(); //开始信号
- return dht_Check(); //检测是否有响应
- }
- int main(void)
- {
-
- uint32_t err_code;
-
- bsp_board_init(BSP_INIT_LEDS);
-
- const app_uart_comm_params_t comm_params =
- {
- RX_PIN_NUMBER,
- TX_PIN_NUMBER,
- RTS_PIN_NUMBER,
- CTS_PIN_NUMBER,
- UART_HWFC,
- false,
- #if defined (UART_PRESENT)
- NRF_UART_BAUDRATE_115200
- #else
- NRF_UARTE_BAUDRATE_115200
- #endif
- };
-
- APP_UART_FIFO_INIT(&comm_params,
- UART_RX_BUF_SIZE,
- UART_TX_BUF_SIZE,
- uart_error_handle,
- APP_IRQ_PRIORITY_LOWEST,
- err_code);
-
- APP_ERROR_CHECK(err_code);
-
- #ifndef ENABLE_LOOPBACK_TEST
-
- int32_t timval;
- timer0_init();
- timer1_init();
- timer2_init();
- ppi_init();
- //启动定时器0
- nrf_drv_timer_enable(&timer0);
-
- while(dht_Init())
- {
- nrf_delay_ms(1000);
- }
- printf("succed\r\n");
- uint8_t temp;
- uint8_t humi;
-
-
- while (true)
- {
- // //计数器加1
- // nrfx_timer_increment(&timer0);
- // //获取计数值
- // timval = nrfx_timer_capture(&timer0,NRF_TIMER_CC_CHANNEL0);
- // printf("count value:%d\r\n",timval);
- bsp_board_led_invert(1);
- dht_Read_Data(&temp,&humi);
- printf("t:%d h:%d\r\n",temp,humi);
- nrf_delay_ms(1048);
- }
- #else
-
- // This part of the example is just for testing the loopback .
- while (true)
- {
- uart_loopback_test();
- }
- #endif
-
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。