【RTT驱动框架分析06】-pwn驱动框架分析+pwm驱动实现

news2024/10/6 22:33:25

pwm

pwm应用程序开发

访问 PWM 设备API

应用程序通过 RT-Thread 提供的 PWM 设备管理接口来访问 PWM 设备硬件,相关接口如下所示:

函数描述
rt_device_find()根据 PWM 设备名称查找设备获取设备句柄
rt_pwm_set()设置 PWM 周期和脉冲宽度
rt_pwm_enable()使能 PWM 设备
rt_pwm_disable()关闭 PWM 设备

查找 PWM 设备

应用程序根据 PWM 设备名称获取设备句柄,进而可以操作 PWM 设备,查找设备函数如下所示:

rt_device_t rt_device_find(const char* name);
参数描述
name设备名称
返回——
设备句柄查找到对应设备将返回相应的设备句柄
RT_NULL没有找到设备

一般情况下,注册到系统的 PWM 设备名称为 pwm0,pwm1等,使用示例如下所示:

#define PWM_DEV_NAME        "pwm3"  /* PWM 设备名称 */
struct rt_device_pwm *pwm_dev;      /* PWM 设备句柄 */
/* 查找设备 */
pwm_dev = (struct rt_device_pwm *)rt_device_find(PWM_DEV_NAME);

设置 PWM 周期和脉冲宽度

通过如下函数设置 PWM 周期和占空比:

rt_err_t rt_pwm_set(struct rt_device_pwm *device,
                    int channel,
                    rt_uint32_t period,
                    rt_uint32_t pulse);
参数描述
devicePWM 设备句柄
channelPWM 通道
periodPWM 周期时间 (单位纳秒 ns)
pulsePWM 脉冲宽度时间 (单位纳秒 ns)
返回——
RT_EOK成功
-RT_EIOdevice 为空
-RT_ENOSYS设备操作方法为空
其他错误码执行失败

PWM 的通道 channel 可为正数或者负数。因为有的芯片的PWM是具有互补输出功能的,即PWM的某一个通道是可以靠两个引脚来发出一对互补的波形。当通道号为正数的时候,代表使用PWM的正常输出波形引脚;为其负数的时候,代表使用PWM的互补输出波形引脚。

使用示例如下所示:

#define PWM_DEV_CHANNEL       1     /* PWM通道的CH1引脚 */
#define PWM_DEV_CHANNEL      -1     /* PWM通道的CH1N引脚 */

PWM 的输出频率由周期时间 period 决定,例如周期时间为 0.5ms (毫秒),则 period 值为 500000ns(纳秒),输出频率为 2KHz,占空比为 pulse / period,pulse 值不能超过 period。

使用示例如下所示:

#define PWM_DEV_NAME        "pwm3"  /* PWM设备名称 */
#define PWM_DEV_CHANNEL     4       /* PWM通道 */
struct rt_device_pwm *pwm_dev;      /* PWM设备句柄 */
rt_uint32_t period, pulse;

period = 500000;    /* 周期为0.5ms,单位为纳秒ns */
pulse = 0;          /* PWM脉冲宽度值,单位为纳秒ns */
/* 查找设备 */
pwm_dev = (struct rt_device_pwm *)rt_device_find(PWM_DEV_NAME);
/* 设置PWM周期和脉冲宽度 */
rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period, pulse);

使能 PWM 设备

设置好 PWM 周期和脉冲宽度后就可以通过如下函数使能 PWM 设备:

rt_err_t rt_pwm_enable(struct rt_device_pwm *device, int channel);
参数描述
devicePWM 设备句柄
channelPWM 通道 -channel代表互补通道
返回——
RT_EOK设备使能成功
-RT_ENOSYS设备操作方法为空
其他错误码设备使能失败

[!NOTE] 注:这个函数并不会使能PWM互补输出的两个引脚,只能使能其中的某一个引脚(CHx或者CHxN)。

使用示例如下所示:

#define PWM_DEV_NAME        "pwm3"  /* PWM设备名称 */
#define PWM_DEV_CHANNEL     4       /* PWM通道 */
struct rt_device_pwm *pwm_dev;      /* PWM设备句柄 */
rt_uint32_t period, pulse;

period = 500000;    /* 周期为0.5ms,单位为纳秒ns */
pulse = 0;          /* PWM脉冲宽度值,单位为纳秒ns */
/* 查找设备 */
pwm_dev = (struct rt_device_pwm *)rt_device_find(PWM_DEV_NAME);
/* 设置PWM周期和脉冲宽度 */
rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period, pulse);
/* 使能设备 */
rt_pwm_enable(pwm_dev, PWM_DEV_CHANNEL);

关闭 PWM 设备通道

通过如下函数关闭 PWM 设备对应通道。

rt_err_t rt_pwm_disable(struct rt_device_pwm *device, int channel);
参数描述
devicePWM 设备句柄
channelPWM 通道
返回——
RT_EOK设备关闭成功
-RT_EIO设备句柄为空
其他错误码设备关闭失败

[!NOTE] 注:这个函数并不会同时失能PWM互补输出的两个引脚,只能失能其中的某一个引脚(CHx或者CHxN)。

使用示例如下所示:

#define PWM_DEV_NAME        "pwm3"  /* PWM设备名称 */
#define PWM_DEV_CHANNEL     4       /* PWM通道 */
struct rt_device_pwm *pwm_dev;      /* PWM设备句柄 */
rt_uint32_t period, pulse;

period = 500000;    /* 周期为0.5ms,单位为纳秒ns */
pulse = 0;          /* PWM脉冲宽度值,单位为纳秒ns */
/* 查找设备 */
pwm_dev = (struct rt_device_pwm *)rt_device_find(PWM_DEV_NAME);
/* 设置PWM周期和脉冲宽度 */
rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period, pulse);
/* 使能设备 */
rt_pwm_enable(pwm_dev, PWM_DEV_CHANNEL);
/* 关闭设备通道 */
rt_pwm_disable(pwm_dev,PWM_DEV_CHANNEL);

FinSH 命令

设置 PWM 设备的某个通道的周期和占空比可使用命令pwm_set pwm1 1 500000 5000,第一个参数为命令,第二个参数为 PWM 设备名称,第 3 个参数为 PWM 通道,第 4 个参数为周期(单位纳秒),第 5 个参数为脉冲宽度(单位纳秒)。

msh />pwm_set pwm1 1 500000 5000
msh />

使能 PWM 设备的某个通道可使用命令pwm_enable pwm1 1,第一个参数为命令,第二个参数为 PWM 设备名称,第 3 个参数为 PWM 通道。

msh />pwm_enable pwm1 1
msh />

关闭 PWM 设备的某个通道可使用命令pwm_disable pwm1 1,第一个参数为命令,第二个参数为 PWM 设备名称,第 3 个参数为 PWM 通道。

msh />pwm_disable pwm1 1
msh />

PWM 设备使用示例

PWM 设备的具体使用方式可以参考如下示例代码,示例代码的主要步骤如下:

  1. 查找 PWM 设备获取设备句柄。
  2. 设置 PWM 周期和脉冲宽度。
  3. 使能 PWM 设备。
  4. while 循环里每 50 毫秒修改一次脉冲宽度。
  • 将 PWM通道对应引脚和 LED 对应引脚相连,可以看到 LED 不停的由暗变到亮,然后又从亮变到暗。
/*
 * 程序清单:这是一个 PWM 设备使用例程
 * 例程导出了 pwm_led_sample 命令到控制终端
 * 命令调用格式:pwm_led_sample
 * 程序功能:通过 PWM 设备控制 LED 灯的亮度,可以看到LED不停的由暗变到亮,然后又从亮变到暗。
*/

#include <rtthread.h>
#include <rtdevice.h>

#define PWM_DEV_NAME        "pwm3"  /* PWM设备名称 */
#define PWM_DEV_CHANNEL     4       /* PWM通道 */

struct rt_device_pwm *pwm_dev;      /* PWM设备句柄 */

static int pwm_led_sample(int argc, char *argv[])
{
    rt_uint32_t period, pulse, dir;

    period = 500000;    /* 周期为0.5ms,单位为纳秒ns */
    dir = 1;            /* PWM脉冲宽度值的增减方向 */
    pulse = 0;          /* PWM脉冲宽度值,单位为纳秒ns */

    /* 查找设备 */
    pwm_dev = (struct rt_device_pwm *)rt_device_find(PWM_DEV_NAME);
    if (pwm_dev == RT_NULL)
    {
        rt_kprintf("pwm sample run failed! can't find %s device!\n", PWM_DEV_NAME);
        return RT_ERROR;
    }

    /* 设置PWM周期和脉冲宽度默认值 */
    rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period, pulse);
    /* 使能设备 */
    rt_pwm_enable(pwm_dev, PWM_DEV_CHANNEL);

    while (1)
    {
        rt_thread_mdelay(50);
        if (dir)
        {
            pulse += 5000;      /* 从0值开始每次增加5000ns */
        }
        else
        {
            pulse -= 5000;      /* 从最大值开始每次减少5000ns */
        }
        if (pulse >= period)
        {
            dir = 0;
        }
        if (0 == pulse)
        {
            dir = 1;
        }

        /* 设置PWM周期和脉冲宽度 */
        rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period, pulse);
    }
}
/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(pwm_led_sample, pwm sample);

pwm驱动框架分析

  1. pwm 驱动框架仅仅需要注册一个控制函数即可

  2. pwm驱动根据命令的类型实现pwm控制

    pwm 功能:
    1.pwm 开启和关闭
    2.设置pwm的周期和占空比
    3.设置pwm的死区时间
    4.设置pwm的相位
    5.pwm互补输出相关

drv_pwm.h分析

/*
 * Copyright (c) 2006-2023, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2018-05-07     aozima       the first version
 * 2022-09-24     yuqi         add phase and dead time configuration
 */

#ifndef __DRV_PWM_H_INCLUDE__
#define __DRV_PWM_H_INCLUDE__

#include <rtthread.h>
//pwm 使能和关闭
#define PWM_CMD_ENABLE      (RT_DEVICE_CTRL_BASE(PWM) + 0)
#define PWM_CMD_DISABLE     (RT_DEVICE_CTRL_BASE(PWM) + 1)
//
#define PWM_CMD_SET         (RT_DEVICE_CTRL_BASE(PWM) + 2)
#define PWM_CMD_GET         (RT_DEVICE_CTRL_BASE(PWM) + 3)
//pwm互补使能和关闭
#define PWMN_CMD_ENABLE     (RT_DEVICE_CTRL_BASE(PWM) + 4)
#define PWMN_CMD_DISABLE    (RT_DEVICE_CTRL_BASE(PWM) + 5)
//设置周期和占空比
#define PWM_CMD_SET_PERIOD  (RT_DEVICE_CTRL_BASE(PWM) + 6)
#define PWM_CMD_SET_PULSE   (RT_DEVICE_CTRL_BASE(PWM) + 7)
//设置死区时间
#define PWM_CMD_SET_DEAD_TIME  (RT_DEVICE_CTRL_BASE(PWM) + 8)
//设置相位
#define PWM_CMD_SET_PHASE   (RT_DEVICE_CTRL_BASE(PWM) + 9)
//pwm 中断开启和关闭
#define PWM_CMD_ENABLE_IRQ  (RT_DEVICE_CTRL_BASE(PWM) + 10)
#define PWM_CMD_DISABLE_IRQ  (RT_DEVICE_CTRL_BASE(PWM) + 11)

struct rt_pwm_configuration
{
    rt_uint32_t channel; /* 通道 0 ~ n or 0 ~ -n, which depends on specific MCU requirements */
    rt_uint32_t period;  /* 周期 单位是 unit:ns 1ns~4.29s:1Ghz~0.23hz */
    rt_uint32_t pulse;   /* 占空比 unit:ns (pulse<=period) */
    rt_uint32_t dead_time;  /* 死区时间 unit:ns */
    rt_uint32_t phase;  /*unit:  相位 degree, 0~360, which is the phase of pwm output, */
    /*
     * RT_TRUE  : The channel of pwm is complememtary.
     * RT_FALSE : The channel of pwm is nomal.
    */
    rt_bool_t  complementary;//互补输出使能
};

struct rt_device_pwm;
/*
	cmd 命令类型
	arg 参数
*/
struct rt_pwm_ops
{
    rt_err_t (*control)(struct rt_device_pwm *device, int cmd, void *arg);
};
//pwm 驱动抽象模型
struct rt_device_pwm
{
    struct rt_device parent;
    const struct rt_pwm_ops *ops;
};


//pwm驱动注册函数
rt_err_t rt_device_pwm_register(struct rt_device_pwm *device, const char *name, const struct rt_pwm_ops *ops, const void *user_data);



/*
	pwm 功能:
	1.pwm 开启和关闭
	2.设置pwm的周期和占空比
	3.设置pwm的死区时间
	4.设置pwm的相位
*/

rt_err_t rt_pwm_enable(struct rt_device_pwm *device, int channel);
rt_err_t rt_pwm_disable(struct rt_device_pwm *device, int channel);
rt_err_t rt_pwm_set(struct rt_device_pwm *device, int channel, rt_uint32_t period, rt_uint32_t pulse);
rt_err_t rt_pwm_set_period(struct rt_device_pwm *device, int channel, rt_uint32_t period);
rt_err_t rt_pwm_set_pulse(struct rt_device_pwm *device, int channel, rt_uint32_t pulse);
rt_err_t rt_pwm_set_dead_time(struct rt_device_pwm *device, int channel, rt_uint32_t dead_time);
rt_err_t rt_pwm_set_phase(struct rt_device_pwm *device, int channel, rt_uint32_t phase);

#endif /* __DRV_PWM_H_INCLUDE__ */

pwm 函数调用流程

函数的调用流程就是从上到下的调用
在这里插入图片描述

编写pwm驱动

时钟

在这里插入图片描述

KCONFIG 配置

board 文件夹下的 KCONFIG 配置

menuconfig BSP_USING_PWM
        bool "Enable PWM BUS"
        default n
        select RT_USING_PWM
        if BSP_USING_PWM
            config BSP_USING_PWM1
                bool "Enable PWM1 BUS"
                default n

            config BSP_USING_PWM2
                bool "Enable PWM2 BUS"
                default n
			config BSP_USING_PWM3
                bool "Enable PWM3 BUS"
                default n
        endif

修改完成以后menuconfig出现pwm配置选项

在这里插入图片描述

添加drv_pwm.c 和drv_pwm.h

这2个文件参考自stm32的pwm驱动函数
在这里插入图片描述

修改libraries\n32_drivers\SConscript文件增加drv_pwm.c的编译

if GetDepend(['RT_USING_PWM']):
    src += ['drv_pwm.c']

在这里插入图片描述

自己实现pwm驱动

由于N32L406系列官方的bsp没得陪我们驱动,我就自己实现一个

drv_pwm.h

这个主要是一个配置文件,配置每个pwm通道的变量

/*
 * Copyright (c) 2006-2022, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2022-10-19     Nations      first version
 */

#ifndef __DRV_PWM_H__
#define __DRV_PWM_H__

#include <rthw.h>
#include <rtthread.h>
#include "board.h"

#ifdef __cplusplus
extern "C" {
#endif
	
#ifdef BSP_USING_PWM1
#define PWM1_CONFIG\
			{\
				.tim					=TIM9,\
				.channel				=0,\
				.gpio_grp				=GPIOB,\
				.pin					=GPIO_PIN_12,\
				.prescaler				=32-1,			\
				.gpio_af				=GPIO_AF1_TIM9,	\
				.name					="pwm0",\
			}
#endif 

#ifdef BSP_USING_PWM2
#define PWM2_CONFIG                     \
			{							\
		.tim					=TIM9,	\
		.channel				=1, 	\
		.gpio_grp				=GPIOB, \
		.pin					=GPIO_PIN_13,	\
		.prescaler				=32-1,			\
		.gpio_af				=GPIO_AF1_TIM9,	\
		.name					="pwm1",		\
			}
#endif /* BSP_USING_PWM2 */

#ifdef BSP_USING_PWM3
#define PWM2_CONFIG                     \
			{							\
		.tim					=TIM9,	\
		.channel				=2, 	\
		.gpio_grp				=GPIOB, \
		.pin					=GPIO_PIN_14,	\
		.prescaler				=32-1,			\
		.gpio_af				=GPIO_AF1_TIM9,	\
		.name					="pwm2",		\
			}
#endif /* BSP_USING_PWM3 */

#ifdef __cplusplus
}
#endif

#endif /* __DRV_PWM_H__ */

drv_pwm.c

/*
 * Copyright (c) 2006-2023, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2018-12-13     zylx         first version
 * 2021-01-23     thread-liu   Fix the timer clock frequency doubling problem
 */

#include <board.h>
#include <rtconfig.h>
#include <rtconfig.h>

#include <drivers/rt_drv_pwm.h>
#include <drv_pwm.h>
#include "n32l40x_tim.h"
#include "n32l40x_rcc.h"

#ifdef BSP_USING_PWM
#ifdef PWM_DEBUG
#define LOG_E rt_kprintf
#define LOG_D LOG_E
#else
int printf_log(const char * fmt,...) {;}
#define LOG_E printf_log
#define LOG_D LOG_E

#endif

struct n32_pwm
{
    struct rt_device_pwm pwm_device;//设备驱动框架的pwm对象
    TIM_Module *tim;//定时器指针
    rt_uint16_t channel;//定时器通道
    GPIO_Module* gpio_grp;//pwm 使用的gpio分组
    uint16_t pin;//pwm 使用的gpio pin
    uint16_t prescaler; //定时器预分频系数
    uint8_t gpio_af;//引脚复用功能
    char *name;//pwm名字也是驱动的名字

};

static struct n32_pwm n32_pwm_obj[] =
{
#ifdef BSP_USING_PWM1
    PWM1_CONFIG,
#endif

#ifdef BSP_USING_PWM2
    PWM2_CONFIG,
#endif

#ifdef BSP_USING_PWM3
    PWM3_CONFIG,
#endif

#ifdef BSP_USING_PWM4
    PWM4_CONFIG,
#endif

#ifdef BSP_USING_PWM5
    PWM5_CONFIG,
#endif

#ifdef BSP_USING_PWM6
    PWM6_CONFIG,
#endif

#ifdef BSP_USING_PWM7
    PWM7_CONFIG,
#endif

#ifdef BSP_USING_PWM8
    PWM8_CONFIG,
#endif

#ifdef BSP_USING_PWM9
    PWM9_CONFIG,
#endif

#ifdef BSP_USING_PWM10
    PWM10_CONFIG,
#endif

#ifdef BSP_USING_PWM11
    PWM11_CONFIG,
#endif

#ifdef BSP_USING_PWM12
    PWM12_CONFIG,
#endif

#ifdef BSP_USING_PWM13
    PWM13_CONFIG,
#endif

#ifdef BSP_USING_PWM14
    PWM14_CONFIG,
#endif

#ifdef BSP_USING_PWM15
    PWM15_CONFIG,
#endif

#ifdef BSP_USING_PWM16
    PWM16_CONFIG,
#endif

#ifdef BSP_USING_PWM17
    PWM17_CONFIG,
#endif
};

static rt_uint64_t tim_clock_get(TIM_Module *htim)
{

    RCC_ClocksType RCC_Clocks;

    RCC_GetClocksFreqValue(&RCC_Clocks);

    LOG_D("%s %d \r\n",__FUNCTION__,__LINE__);
    if(htim==TIM1||htim==TIM8)
    {
        return RCC_Clocks.Pclk2Freq;
    }
    else
    {
        return RCC_Clocks.Pclk1Freq;
    }
    return 0;
}


//输出使能控制
static rt_err_t drv_pwm_enable(TIM_Module *time, struct rt_pwm_configuration *configuration, rt_bool_t enable)
{
    /* Converts the channel number to the channel number of Hal library */
    LOG_D("%s %d ch = %d \r\n",__FUNCTION__,__LINE__,configuration->channel);
    int x=0;

    if(enable)
    {
        x= (0x01<<(4*configuration->channel));
        time->CCEN |=(0x01<<(4*configuration->channel));
    }
    else
    {
        x= ~(0x01<<(4*configuration->channel));
        time->CCEN &= ~(0x01<<(4*configuration->channel));
    }

    LOG_D("%s %d CCEN = %x x = %x\r\n",__FUNCTION__,__LINE__,time->CCEN,x);
    return RT_EOK;
}
//互补输出使能控制
static rt_err_t drv_pwmn_enable(TIM_Module *time, struct rt_pwm_configuration *configuration, rt_bool_t enable)
{
    /* Converts the channel number to the channel number of Hal library */
    (void)enable;
    LOG_D("%s %d ch=%d\r\n",__FUNCTION__,__LINE__,configuration->channel);
    if(TIM1==time || TIM8==time)
    {
        if(enable)
        {
            time->CCEN |=(0x02<<(4*configuration->channel));
        }
        else
        {
            time->CCEN &= !(0x02<<(4*configuration->channel));
        }
    }

    return RT_EOK;
}
static rt_err_t drv_pwm_get(TIM_Module *htim, struct rt_pwm_configuration *configuration)
{

    LOG_D("%s %d \r\n",__FUNCTION__,__LINE__);
    configuration->period = TIM_GetAutoReload(htim);
    switch(configuration->channel)
    {
    case 0:
        configuration->pulse =TIM_GetCap1(htim);
        break;
    case 1:
        configuration->pulse =TIM_GetCap2(htim);
        break;
    case 2:
        configuration->pulse =TIM_GetCap3(htim);
        break;
    case 3:
        configuration->pulse =TIM_GetCap4(htim);
        break;
    default:
        configuration->pulse =0;
        break;


    }

    return RT_EOK;
}

static rt_err_t drv_pwm_set(TIM_Module *htim, struct rt_pwm_configuration *configuration)
{

    LOG_D("%s %d ch=%d\r\n",__FUNCTION__,__LINE__,configuration->channel);
    TIM_SetAutoReload(htim,configuration->period);
    switch(configuration->channel)
    {
    case 0:
        TIM_SetCmp1(htim,configuration->pulse);
        break;
    case 1:
        TIM_SetCmp2(htim,configuration->pulse);
        break;
    case 2:
        TIM_SetCmp3(htim,configuration->pulse);
        break;
    case 3:
        TIM_SetCmp4(htim,configuration->pulse);
        break;
    default:
        configuration->pulse =0;
        break;


    }
    return RT_EOK;
}

static rt_err_t drv_pwm_set_period(TIM_Module *htim, struct rt_pwm_configuration *configuration)
{
    LOG_D("%s %d \r\n",__FUNCTION__,__LINE__);
    TIM_SetAutoReload(htim,configuration->period);

    return RT_EOK;
}

static rt_err_t drv_pwm_set_pulse(TIM_Module *htim, struct rt_pwm_configuration *configuration)
{
    //	LOG_D("%s %d \r\n",__FUNCTION__,__LINE__);
    switch(configuration->channel)
    {
    case 0:
        TIM_SetCmp1(htim,configuration->pulse);
        LOG_D("%s %d TIM_SetCmp1 \r\n",__FUNCTION__,__LINE__);
        break;
    case 1:
        TIM_SetCmp2(htim,configuration->pulse);
        break;
    case 2:
        TIM_SetCmp3(htim,configuration->pulse);
        break;
    case 3:
        TIM_SetCmp4(htim,configuration->pulse);
        break;
    default:
        configuration->pulse =0;
        break;


    }


    return RT_EOK;
}

static rt_err_t drv_pwm_control(struct rt_device_pwm *device, int cmd, void *arg)
{
    struct rt_pwm_configuration *configuration = (struct rt_pwm_configuration *)arg;
    struct n32_pwm *pwm = (struct n32_pwm  *)device->parent.user_data;
    LOG_D("%s %d cmd = %d\r\n",__FUNCTION__,__LINE__,cmd);
    switch (cmd)
    {
    case PWM_CMD_ENABLE:
        return drv_pwm_enable(pwm->tim, configuration, RT_TRUE);
    case PWM_CMD_DISABLE:
        return drv_pwm_enable(pwm->tim, configuration, RT_FALSE);
    case PWM_CMD_SET:
        return drv_pwm_set(pwm->tim, configuration);
    case PWM_CMD_SET_PERIOD:
        return drv_pwm_set_period(pwm->tim, configuration);
    case PWM_CMD_SET_PULSE:
        return drv_pwm_set_pulse(pwm->tim, configuration);
    case PWM_CMD_GET:
        return drv_pwm_get(pwm->tim, configuration);
    default:
        return -RT_EINVAL;
    }
}
/**
 * @brief  配置时钟
 */
static void pwm_time_rcc_config(TIM_Module *htim,GPIO_Module *gpio_grp)
{
    LOG_D("%s %d \r\n",__FUNCTION__,__LINE__);
    if(TIM1==htim)
    {
        RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_TIM1, ENABLE);
    }
    else if(TIM2==htim)
    {
        RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_TIM2, ENABLE);
    }
    else if(TIM3==htim)
    {
        RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_TIM3, ENABLE);
    }
    else if(TIM4==htim)
    {
        RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_TIM4, ENABLE);
    }
    else if(TIM5==htim)
    {
        RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_TIM5, ENABLE);
    }
    else if(TIM6==htim)
    {
        RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_TIM6, ENABLE);
    }
    else if(TIM7==htim)
    {
        RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_TIM7, ENABLE);
    }
    else if(TIM8==htim)
    {
        RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_TIM8, ENABLE);
    }
    else if(TIM9==htim)
    {
        RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_TIM9, ENABLE);
        LOG_D("%s %d RCC_EnableAPB1PeriphClk TIM9 \r\n",__FUNCTION__,__LINE__);
    }

    if(GPIOA==gpio_grp)
    {
        RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOA, ENABLE);
    }
    else if(GPIOB==gpio_grp)
    {
        RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOB, ENABLE);
        LOG_D("%s %d RCC_EnableAPB2PeriphClk GPIOB \r\n",__FUNCTION__,__LINE__);
    }
    else if(GPIOC==gpio_grp)
    {
        RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOC, ENABLE);
    }
    else if(GPIOD==gpio_grp)
    {
        RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOD, ENABLE);
    }

}



/**
 * @brief  配置pwm输出对应的gpio引脚
 */
static void pwm_time_gpio_config(struct n32_pwm *htim)

{
    LOG_D("%s %d \r\n",__FUNCTION__,__LINE__);
    GPIO_InitType GPIO_InitStructure;
    GPIO_InitStruct(&GPIO_InitStructure);
    GPIO_InitStructure.Pin        = htim->pin;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Current = GPIO_DC_4mA;
    GPIO_InitStructure.GPIO_Alternate = htim->gpio_af;
    GPIO_InitPeripheral(htim->gpio_grp, &GPIO_InitStructure);
}


static rt_err_t n32_hw_pwm_init(struct n32_pwm *device)
{
    LOG_D("%s %d \r\n",__FUNCTION__,__LINE__);
    rt_err_t result = RT_EOK;
    TIM_TimeBaseInitType TIM_TimeBaseStructure;
    OCInitType TIM_OCInitStructure;

    RT_ASSERT(device != RT_NULL);

    pwm_time_rcc_config(device->tim,device->gpio_grp);
    pwm_time_gpio_config(device);

    TIM_TimeBaseStructure.Period	= 100;
    TIM_TimeBaseStructure.Prescaler = device->prescaler;
    TIM_TimeBaseStructure.ClkDiv    = 0;
    TIM_TimeBaseStructure.CntMode   = TIM_CNT_MODE_UP;

    TIM_InitTimeBase(device->tim, &TIM_TimeBaseStructure);


    /* PWM1 Mode configuration: Channel1 */
    TIM_OCInitStructure.OcMode      = TIM_OCMODE_PWM1;
    TIM_OCInitStructure.OutputState = TIM_OUTPUT_STATE_ENABLE;
    TIM_OCInitStructure.Pulse       = 0;
    TIM_OCInitStructure.OcPolarity  = TIM_OC_POLARITY_LOW;

    switch(device->channel)
    {
    case 0:
        TIM_InitOc1(device->tim, &TIM_OCInitStructure);
        TIM_ConfigOc1Preload(device->tim, TIM_OC_PRE_LOAD_ENABLE);
        LOG_D("%s %d PWM CH0 CFG\r\n",__FUNCTION__,__LINE__);
        break;
    case 1:
        TIM_InitOc2(device->tim, &TIM_OCInitStructure);
        TIM_ConfigOc2Preload(device->tim, TIM_OC_PRE_LOAD_ENABLE);
        break;
    case 2:
        TIM_InitOc3(device->tim, &TIM_OCInitStructure);
        TIM_ConfigOc3Preload(device->tim, TIM_OC_PRE_LOAD_ENABLE);
        break;
    case 3:
        TIM_InitOc4(device->tim, &TIM_OCInitStructure);
        TIM_ConfigOc4Preload(device->tim, TIM_OC_PRE_LOAD_ENABLE);
        break;
    default:

        break;


    }


    TIM_ConfigArPreload(device->tim, ENABLE);
    TIM_Enable(device->tim, ENABLE);

    if(	device->tim == TIM1 || device->tim== TIM8 )
    {

        TIM_EnableCtrlPwmOutputs(device->tim, ENABLE);
    }

    LOG_D("%s 32_pwm_init", device->name);

    return result;
}

static rt_err_t drv_pwm_control(struct rt_device_pwm *device, int cmd, void *arg);
static struct rt_pwm_ops drv_ops =
{
    drv_pwm_control
};

static int n32_pwm_init(void)
{
    int i = 0;
    int result = RT_EOK;



    for (i = 0; i < sizeof(n32_pwm_obj) / sizeof(n32_pwm_obj[0]); i++)
    {
        /* pwm init */
        if (n32_hw_pwm_init(&n32_pwm_obj[i]) != RT_EOK)
        {
            LOG_E("%s init failed", n32_pwm_obj[i].name);
            result = -RT_ERROR;
            goto __exit;
        }
        else
        {
            LOG_D("%s init success", n32_pwm_obj[i].name);

            /* register pwm device */
            if (rt_device_pwm_register(&n32_pwm_obj[i].pwm_device, n32_pwm_obj[i].name, &drv_ops, n32_pwm_obj+i) == RT_EOK)
            {
                LOG_D("%s register success", n32_pwm_obj[i].name);
            }
            else
            {
                LOG_E("%s register failed", n32_pwm_obj[i].name);
                result = -RT_ERROR;
            }
        }
    }

__exit:
    return result;
}
INIT_DEVICE_EXPORT(n32_pwm_init);

#endif /* BSP_USING_PWM */

pwm 驱动测试

pwm驱动框架提供了pwm测试函数

在这里插入图片描述

pwm官方的框架测试函数代码


#ifdef RT_USING_FINSH
#include <stdlib.h>
#include <string.h>
#include <finsh.h>

static int pwm(int argc, char **argv)
{
    rt_err_t result = -RT_ERROR;
    char *result_str;
    static struct rt_device_pwm *pwm_device = RT_NULL;
    struct rt_pwm_configuration cfg = {0};

    if(argc > 1)
    {
        if(!strcmp(argv[1], "probe"))
        {
            if(argc == 3)
            {
                pwm_device = (struct rt_device_pwm *)rt_device_find(argv[2]);
                result_str = (pwm_device == RT_NULL) ? "failure" : "success";
                rt_kprintf("probe %s %s\n", argv[2], result_str);
            }
            else
            {
                rt_kprintf("pwm probe <device name>                  - probe pwm by name\n");
            }
        }
        else
        {
            if(pwm_device == RT_NULL)
            {
                rt_kprintf("Please using 'pwm probe <device name>' first.\n");
                return -RT_ERROR;
            }
            if(!strcmp(argv[1], "enable"))
            {
                if(argc == 3)
                {
                    result = rt_pwm_enable(pwm_device, atoi(argv[2]));
                    result_str = (result == RT_EOK) ? "success" : "failure";
                    rt_kprintf("%s channel %d is enabled %s \n", pwm_device->parent.parent.name, atoi(argv[2]), result_str);
                }
                else
                {
                    rt_kprintf("pwm enable <channel>                     - enable pwm channel\n");
                    rt_kprintf("    e.g. MSH >pwm enable  1              - PWM_CH1  nomal\n");
                    rt_kprintf("    e.g. MSH >pwm enable -1              - PWM_CH1N complememtary\n");
                }
            }
            else if(!strcmp(argv[1], "disable"))
            {
                if(argc == 3)
                {
                    result = rt_pwm_disable(pwm_device, atoi(argv[2]));
                }
                else
                {
                    rt_kprintf("pwm disable <channel>                    - disable pwm channel\n");
                }
            }
            else if(!strcmp(argv[1], "get"))
            {
                cfg.channel = atoi(argv[2]);
                result = rt_pwm_get(pwm_device, &cfg);
                if(result == RT_EOK)
                {
                    rt_kprintf("Info of device [%s] channel [%d]:\n",pwm_device, atoi(argv[2]));
                    rt_kprintf("period      : %d\n", cfg.period);
                    rt_kprintf("pulse       : %d\n", cfg.pulse);
                    rt_kprintf("Duty cycle  : %d%%\n",(int)(((double)(cfg.pulse)/(cfg.period)) * 100));
                }
                else
                {
                    rt_kprintf("Get info of device: [%s] error.\n", pwm_device);
                }
            }
            else if (!strcmp(argv[1], "set"))
            {
                if(argc == 5)
                {
                    result = rt_pwm_set(pwm_device, atoi(argv[2]), atoi(argv[3]), atoi(argv[4]));
                    rt_kprintf("pwm info set on %s at channel %d\n",pwm_device,(rt_base_t)atoi(argv[2]));
                }
                else
                {
                    rt_kprintf("Set info of device: [%s] error\n", pwm_device);
                    rt_kprintf("Usage: pwm set <channel> <period> <pulse>\n");
                }
            }
            else if(!strcmp(argv[1], "phase"))
            {
                if(argc == 4)
                {
                    result = rt_pwm_set_phase(pwm_device, atoi(argv[2]),atoi(argv[3]));
                    result_str = (result == RT_EOK) ? "success" : "failure";
                    rt_kprintf("%s phase is set %d \n", pwm_device->parent.parent.name, (rt_base_t)atoi(argv[3]));
                }
            }
			else if(!strcmp(argv[1], "pulse"))
            {
                if(argc == 4)
                {
                    result = rt_pwm_set_pulse(pwm_device, atoi(argv[2]),atoi(argv[3]));
                    result_str = (result == RT_EOK) ? "success" : "failure";
                    rt_kprintf("%s pulse is set %d \n", pwm_device->parent.parent.name, (rt_base_t)atoi(argv[3]));
                }
            }
            else if(!strcmp(argv[1], "dead_time"))
            {
                if(argc == 4)
                {
                    result = rt_pwm_set_dead_time(pwm_device, atoi(argv[2]),atoi(argv[3]));
                    result_str = (result == RT_EOK) ? "success" : "failure";
                    rt_kprintf("%s dead_time is set %d \n", pwm_device->parent.parent.name, (rt_base_t)atoi(argv[3]));
                }
            }
            else
            {
                rt_kprintf("Usage: \n");
                rt_kprintf("pwm probe      <device name>                - probe pwm by name\n");
                rt_kprintf("pwm enable     <channel>                    - enable pwm channel\n");
                rt_kprintf("pwm disable    <channel>                    - disable pwm channel\n");
                rt_kprintf("pwm get        <channel>                    - get pwm channel info\n");
                rt_kprintf("pwm set        <channel> <period> <pulse>   - set pwm channel info\n");
				rt_kprintf("pwm pulse      <channel> <pulse>            - set pwm pulse\n");
                rt_kprintf("pwm phase      <channel> <phase>            - set pwm phase\n");
                rt_kprintf("pwm dead_time  <channel> <dead_time>        - set pwm dead time\n");
                result = -RT_ERROR;
            }
        }
    }
    else
    {
        rt_kprintf("Usage: \n");
        rt_kprintf("pwm probe      <device name>               - probe pwm by name\n");
        rt_kprintf("pwm enable     <channel>                   - enable pwm channel\n");
        rt_kprintf("pwm disable    <channel>                   - disable pwm channel\n");
        rt_kprintf("pwm get        <channel>                   - get pwm channel info\n");
        rt_kprintf("pwm set        <channel> <period> <pulse>  - set pwm channel info\n");
		rt_kprintf("pwm pulse      <channel> <pulse>           - set pwm pulse\n");
        rt_kprintf("pwm phase      <channel> <phase>           - set pwm phase\n");
        rt_kprintf("pwm dead_time  <channel> <dead_time>       - set pwm dead time\n");
        result = -RT_ERROR;
    }

    return RT_EOK;
}
MSH_CMD_EXPORT(pwm, pwm [option]);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/859702.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

橙河网络:2023年,我看谁还在做实体行业?

大家好&#xff0c;我是橙河老师&#xff0c;今天讲一讲实体行业。 现在实体行业还好干吗&#xff1f; 肯定是不好干了。 别的不扯&#xff0c;这几年很多大佬&#xff0c;能把老百姓干的事儿都干了。 一天收入上百万的演员&#xff0c;在直播间卖着九块九的东西&#xff0…

学习笔记-JVM-工具包(JVM分析工具)

常用工具 JDK工具 ① jps: JVM Process status tool&#xff1a;JVM进程状态工具&#xff0c;查看进程基本信息 ② jstat: JVM statistics monitoring tool &#xff1a; JVM统计监控工具&#xff0c;查看堆&#xff0c;GC详细信息 ③ jinfo&#xff1a;Java Configuration I…

MATLAB实现两组数据的延时对齐效果

博主在某次实验中&#xff0c;相同的实验条件下分别采集了两组数据&#xff0c;发现两组数据存在一个延时&#xff0c;如下图所示&#xff1a; 本文记录消除这个延时&#xff0c;实现相同数据状态的对齐效果&#xff0c;采用MATLAB自带的xcorr函数实现&#xff0c;具体步骤如下…

vteam透明屏,在场景化应用中,有哪些特点表现?

vteam透明屏是一种新型的显示技术&#xff0c;它采用透明材料制成&#xff0c;可以在显示内容的同时保持背景的透明度。 这种屏幕可以应用于各种领域&#xff0c;如广告、零售、展览等&#xff0c;具有很大的潜力和市场前景。 vteam透明屏的特点之一是其高透明度。与传统的显…

五、web应用程序技术——web功能

文章目录 一、服务器端功能1.1 SQL1.2 XML1.3 web服务 二、客户端功能2.1 HTML2.2 超链接2.3 表单2.4 CSS2.5 JavaScript2.6 文档对象模型2.7 Ajax2.8 JSON2.9 同源策略2.10浏览器拓展技术 一、服务器端功能 早期的web站点由各种静态资源组成&#xff0c;如HTML页面与图片。当用…

主数据管理案例-中国外运

1、 背景介绍及难点分析 作为世界领先的物流行业整合商、端到端的全程供应链解决方案和一站式物流服务提供商&#xff0c;中国外运非常重视信息化建设&#xff0c;先后投资建设了 300多个信息系统&#xff0c;为中国外运的内部管理和业务运作提供 IT 支持和保障。 由于缺乏统一…

当我准备出门时,发现了......我可以用Python实现12306自动买票

前言 不知道大家有没有之前碰到这样的情况&#xff0c;打算去某一个地方当你规划好了时间准备去买票的时候&#xff0c;你想要的那一列往往没有你想要的票了&#xff0c;尤其是国庆七天假和春节半月假&#xff0c;有时候甚至买不到规定计划时间内的票&#xff0c;真的是太烦躁…

数据分析-python学习 (1)numpy相关

内容为&#xff1a;https://juejin.cn/book/7240731597035864121的学习笔记 导包 import numpy as np numpy数组创建 创建全0数组&#xff0c;正态分布、随机数组等就不说了&#xff0c;提供了相应的方法通过已有数据创建有两种 arr1np.array([1,2,3,4,5]) 或者datanp.loadt…

vxe-grid\table 自定义动态列排序设置

实现动态加载的表格数据&#xff0c;根据设置动态控制指定的某些字段排序功能&#xff1b;如下图所示&#xff1b; 代码实现&#xff1a;标签内添加属性&#xff1b; :sort-config"{trigger:cell, defaultSort: {field: , order: desc}, orders:[desc, asc]}" sort-…

LinuxC编程——线程

目录 一、概念二、进程与线程的区别⭐⭐⭐三、线程资源四、函数接口4.1 线程创建4.2 线程退出4.3 线程回收4.3.1 阻塞回收4.3.2 非阻塞回收 4.4 pthread_create之传参4.5 练习 一、概念 是一个轻量级的进程&#xff0c;为了提高系统的性能引入线程。 进程与线程都参与cpu的统一…

这些可以将音频翻译成文字的工具你值得拥有

你是否曾经遇到过这样的情景&#xff1a;你收到了一段包含外国人发言内容的会议音频&#xff0c;但是由于自己的外语水平一般而却无法理解其中的外语内容&#xff1f;别担心&#xff0c;现在有一些神奇的翻译音频文件的软件可以帮助你解决这个问题&#xff01;如果你还不知道翻…

电脑必备的3款简单好用小工具,建议收藏使用!

​在当今社会&#xff0c;电脑已成为我们工作、学习和娱乐的重要工具。除了拥有一台电脑&#xff0c;一些小工具的加入可以大大提升我们的效率和使用体验。 整理了 3 款比较实用的软件&#xff0c;个人觉得非常实用。&#xff0c;它们将让你的电脑使用更加便捷、高效。不管你是…

使用Prisma访问数据库

首先&#xff0c;确保你已经安装了 Prisma CLI。你可以使用以下命令进行安装&#xff1a; npm install prisma --save-dev接下来&#xff0c;你需要初始化 Prisma 项目&#xff0c;最后一个参数需要指定数据库类型&#xff0c;如postgresql&#xff0c;sqlist&#xff0c;mysql…

深入浅出对话系统——检索式对话系统进阶方法

引言 本文介绍检索式对话系统进阶方法&#xff0c;主要介绍两篇论文工作。 Fine-grained Post-training for Improving Retrieval-based Dialogue Systems 这里的post-training是定义在pre-training和fine-turning中间的阶段&#xff0c;具体的思想是用一些特定领域的数据去…

ControlNet

2023.8.10 Adding Conditional Control to Text-to-Image Diffusion Models 对于 T2I 扩散模型添加 条件控制 相关联比较大的几篇论文&#xff1a; 0.Abstract 可以为预训练的扩散模型提供额外的输入条件控制。不需要很多计算资源。 即使在小数据集上&#xff0c;也具有鲁棒…

中间人攻击与 RADIUS 身份验证

在数字时代&#xff0c;中间人&#xff08;MitM&#xff09;攻击已成为一种日益严重的威胁。根据网络安全风险投资公司的网络安全统计数据&#xff0c;预计到2025年&#xff0c;网络犯罪每年将给世界造成10.5万亿美元的损失&#xff0c;比2015年的3万亿美元大幅增加。这种令人震…

企业微信消息模板通过中转页面(h5)判断跳转pc还是跳小程序

需求&#xff1a;甲方要根据不同的端跳转不同端的详情页面&#xff0c;为什么要这样&#xff0c;是应为每次在PC端点击消息&#xff0c;都要扫码登录进入小程序&#xff0c;不想进入小程序&#xff0c;要打开PC端 1、在pc端的微信中点击消息&#xff0c;则打开PC后台管理系统 …

Telegram营销,全球跨境电商都在研究的营销策略

Telegram 目前有7 亿月活跃用户。作为一个如此流行和广泛的即时通讯平台&#xff0c; Telegram 已成为企业和客户沟通的重要即时通讯工具。 为了使企业能够快速有效地覆盖目标受众&#xff0c;Telegram 不断改进平台&#xff0c;提供一系列功能&#xff0c;例如可定制的自动化…

内网穿透实战应用-——【如何在树莓派上安装cpolar内网穿透】

如何在树莓派上安装cpolar内网穿透 文章目录 如何在树莓派上安装cpolar内网穿透前言1.在树莓派上安装cpolar2.查询cpolar版本号3.激活本地cpolar客户端4.cpolar记入配置文件 前言 树莓派作为一个超小型的电脑系统&#xff0c;虽然因其自身性能所限&#xff0c;无法如台式机或笔…