嵌入式 Linux C ARM 蓝牙BLEnRF52832蓝牙产品开发

蓝牙芯片nRF52832之PWM的使用

2020-02-17  本文已影响0人  YANWeichuan

nRF52832蓝牙芯片,自带了3路PWM硬件模块,每个模块支持4路,总共支持12路PWM。在SDK15中提供了简化的API,能够快速、便捷的实现PWM功能。以驱动LED灯为例,介绍一下nRF5283在SDK15中PWM的使用步骤以及注意的问题。

SDK15 PWM的使用步骤

1. SDK 驱动配置中enable PWM模块

图1

2. 初始化PWM实例

    #include "nrf_drv_pwm.h"
    static nrf_drv_pwm_t m_pwm0 = NRF_DRV_PWM_INSTANCE(0);

3. 初始化配置

    nrf_drv_pwm_config_t const config0 =
    {
        .output_pins =
        {
            BSP_LED_0 | NRF_DRV_PWM_PIN_INVERTED, // channel 0
            BSP_LED_2 | NRF_DRV_PWM_PIN_INVERTED, // channel 1
            BSP_LED_3 | NRF_DRV_PWM_PIN_INVERTED, // channel 2
            BSP_LED_1 | NRF_DRV_PWM_PIN_INVERTED // channel 3
        },
        .irq_priority = APP_IRQ_PRIORITY_LOWEST,
        .base_clock = NRF_PWM_CLK_125kHz,
        .count_mode = NRF_PWM_MODE_UP,
        .top_value = 15625,
        .load_mode = NRF_PWM_LOAD_INDIVIDUAL,
        .step_mode = NRF_PWM_STEP_AUTO
    };
    APP_ERROR_CHECK(nrf_drv_pwm_init(&m_pwm0, &config0, NULL));

每路PWM硬件模块支持4个channel,nrf_drv_pwm_config_t中:

图2
图3

4. 定义sequence数据

    static nrf_pwm_values_individual_t /*const*/ seq_values[] =
    {
        { 0x8000, 0, 0, 0 },
        { 0, 0x8000, 0, 0 },
        { 0, 0, 0x8000, 0 },
        { 0, 0, 0, 0x8000 }
    };

先定义每个通道的占空比,通过clock数来定义,不能超过top_value。如果四个通道均使用,则每个数组均有4个人duty cycle数据,至少有一组数据,如{ 0x8000, 0, 0, 0 },通道1的cycle数是0x8000,2-4均为0。根据需要,可以使用不同的占空比来形成一个序列,示例中就定义了4个序列,seq_values的size为4。每个通道使用独立数据,则在config中用.load_mode = NRF_PWM_LOAD_INDIVIDUAL。

如果所有的通道共用一个sequence数据,则需要使用nrf_pwm_values_common_t,实际就是uint16_t,则在config中用.load_mode = NRF_PWM_LOAD_COMMON。

    nrf_pwm_sequence_t const seq =
    {
        .values.p_individual = seq_values,
        .length = NRF_PWM_VALUES_LENGTH(seq_values),
        .repeats = 0,
        .end_delay = 0
    };

nrf_pwm_sequence_t中,

5. 触发PWM

    (void)nrf_drv_pwm_simple_playback(&m_pwm0, &seq, 1, NRF_DRV_PWM_FLAG_LOOP);

通过play_back来触发PWM,第三个参数为播放的次数,最后一个参数定义模式,NRF_DRV_PWM_FLAG_LOOP为永远重复,NRFX_PWM_FLAG_STOP播放定义的次数后结束,还有其他的模式定义起始seq等(最多可以有两个seq)。

6. 完整的代码

    static void pwm_demo(void)
    {
        nrf_drv_pwm_config_t const config0 =
        {
            .output_pins =
            {
                BSP_LED_0 | NRF_DRV_PWM_PIN_INVERTED, // channel 0
                BSP_LED_2 | NRF_DRV_PWM_PIN_INVERTED, // channel 1
                BSP_LED_3 | NRF_DRV_PWM_PIN_INVERTED, // channel 2
                BSP_LED_1 | NRF_DRV_PWM_PIN_INVERTED // channel 3
            },
            .irq_priority = APP_IRQ_PRIORITY_LOWEST,
            .base_clock = NRF_PWM_CLK_125kHz,
            .count_mode = NRF_PWM_MODE_UP,
            .top_value = 15625,
            .load_mode = NRF_PWM_LOAD_INDIVIDUAL,
            .step_mode = NRF_PWM_STEP_AUTO
        };
        APP_ERROR_CHECK(nrf_drv_pwm_init(&m_pwm0, &config0, NULL));
        static nrf_pwm_values_individual_t /*const*/ seq_values[] =
        {
            { 0x8000, 0, 0, 0 },
            { 0, 0x8000, 0, 0 },
            { 0, 0, 0x8000, 0 },
            { 0, 0, 0, 0x8000 }
        };
        nrf_pwm_sequence_t const seq =
        {
            .values.p_individual = seq_values,
            .length = NRF_PWM_VALUES_LENGTH(seq_values),
            .repeats = 0,
            .end_delay = 0
        };
        (void)nrf_drv_pwm_simple_playback(&m_pwm0, &seq, 1, NRF_DRV_PWM_FLAG_LOOP);
    }
    *注:copy自examples/peripheral/pwm_driver/main.c中的demo5

其他的各种使用demo参考SDK中examples/peripheral/pwm_driver/main.c

PWM输出波形的影响因素

1. 影响PWM输出的波形有config和sequence两个定义。

    nrf_drv_pwm_config_t const config =
    {
        .base_clock,
        .count_mode,
        .top_value,
    };
    
    nrf_pwm_sequence_t const seq =
    {
        .values,
        .repeats,
        .end_delay
    };

说明:

    {
        .base_clock = NRF_PWM_CLK_125kHz,
        .count_mode = NRF_PWM_MODE_UP,
        .top_value = 1000,
        .values = {1000, 0} //一个100%的占空比,一个0%的占空比
        .repeats = 0,
        .end_delay = 0
    }

该组的波形为:

图3

如一组定义:

    {
        .base_clock = NRF_PWM_CLK_125kHz,
        .count_mode = NRF_PWM_MODE_UP,
        .top_value = 1000,
        .values = {1000, 0} //一个100%的占空比,一个0%的占空比
        .repeats = 2,
        .end_delay = 0
    }

该组的波形为:

图4

如一组定义:

    {
        .base_clock = NRF_PWM_CLK_125kHz,
        .count_mode = NRF_PWM_MODE_UP,
        .top_value = 1000,
        .values = {1000, 0} //一个100%的占空比,一个0%的占空比
        .repeats = 0,
        .end_delay = 2
    }

该组的波形为:

图5

2. 官方demo中duty cycle 值为0x8000的调查

官方SDK中examples/peripheral/pwm_driver/main.c示例中,nrf_pwm_sequence_t中的duty cycle数值均用0x8000,一直百思不得其解。不同的例子,有的LED为200ms,有的为250ms,但duty cycle均采用0x8000,并且都能工作。why?

看到nrf_pwm_configure的源码中有个assert,NRFX_ASSERT(top_value <= PWM_COUNTERTOP_COUNTERTOP_Msk),进一步调查发现PWM_COUNTERTOP_COUNTERTOP_Msk定义为0x7FFF。也就是COUNTERTOP是个14位的寄存器,查看官方spec验证了这一想法。

实际上top_value的最大值是0x7FFF,所以nrf_pwm_sequence_t的每个duty cycle值也不应该大于0x7FFF。0x8000刚好比最大值大1,无论top_value是多少,都是占空比100%。猜测,如果设置的值大于0x7FFF,系统应该会自动将其置为0x7FFF。在示例中,将0x8000替换为0x9000,0xA000均能工作,算是做了一个验证。

    __STATIC_INLINE void nrf_pwm_configure(NRF_PWM_Type * p_reg,
                                           nrf_pwm_clk_t  base_clock,
                                           nrf_pwm_mode_t mode,
                                           uint16_t       top_value)
    {
        NRFX_ASSERT(top_value <= PWM_COUNTERTOP_COUNTERTOP_Msk);
    
        p_reg->PRESCALER  = base_clock;
        p_reg->MODE       = mode;
        p_reg->COUNTERTOP = top_value;
    }
    
    #define PWM_COUNTERTOP_COUNTERTOP_Msk (0x7FFFUL << PWM_COUNTERTOP_COUNTERTOP_Pos) /*!< Bit mask of COUNTERTOP field. */

Spec中COUNTERTOP的寄存器定义:

图6

3. COUNTERTOP的限制进一步思考

既然top_value的最大值是0x7FFF(十进制32768),sequence中的值也应该均比0x7FFF小。对于一个base_clock为 NRF_PWM_CLK_125kHz的PWM来说,最大的cycle时间为:32768 * ( 1 / 125000) = 262.144ms。如果需要一个cycle time为1秒的波形该怎么办呢?这时得动用repeats来拓展了。将top_value设为250ms极为 250 / 0.008 = 31250。将repeats设为4,即为250*4 = 1秒了。也可以组合end_delay实现各种不同的波形。

在官方的例子中都是一个占空比100%(values值0x8000),一个占空比为0%的波形(values值0)。可以将top_value设置为小于32768,values中的值设置成0-32768,即可调整占空比。

总结

PWM虽然简单,但是对一个平台来说都有自己的特色。调试下来,对文档中说得不够清楚的,有些古怪的地方还是要调查一下,会对这个点的理解更深入一些。

(原创yanweichuan@163.com

上一篇下一篇

猜你喜欢

热点阅读