【简介】
PWM(Pulse Width Modulation , 脉冲宽度调制) ,在 Rt-thread 把PWM 设备抽象为PWM 设备驱动框架,设备驱动代码为components\drivers\misc\rt_drv_pwm.c ,应用层通过PWM设备驱动层的 rt_pwm_enable,rt_pwm_disable ,rt_pwm_set ,rt_pwm_set_period ,rt_pwm_set_pulse ,rt_pwm_set_dead_time ,rt_pwm_set_phase ,rt_pwm_get 接口调用底层提供的服务接口,rt_device_pwm_register 接口可以将PWM 设备注册到系统中。
rt-thread pwm 设备device 结构体,编写PWM设备驱动基于该结构体派生出新的PWM设备结构体。
struct rt_device_pwm;
struct rt_pwm_ops
{
rt_err_t (*control)(struct rt_device_pwm *device, int cmd, void *arg);
};
struct rt_device_pwm
{
struct rt_device parent;
const struct rt_pwm_ops *ops;
};PWM 设备的配置通过以下结构体进行配置
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;
};PWM 设备驱动的启用在env 的menuconfig 下开启

生成工程后,pwm 驱动框架的已经被加入工程

PWM 驱动适配底层和芯片的接口我们在Kconfig 中添加开启PWM驱动的配置开关
menuconfig BSP_USING_PWM bool "Enable PWM" default n select RT_USING_PWM
并在SConscript 文件中加入编译选项的配置,我们在drv_pwm 文件中适配PWM驱动
# add pwm driver
if GetDepend('BSP_USING_PWM'):
src += ['drv_pwm.c']在menuconfig 中开启PWM 驱动

我们添加如下代码添加PWM设备
/** *****************************************************************************************************
* \file drv_pwm.c *
*******************************************************************************************************/
/********************************************************************************************************
* Include header files *
*******************************************************************************************************/
#include <rtthread.h>
#include <rtdevice.h>
#include "board.h"
/********************************************************************************************************
* Private Macro definition *
*******************************************************************************************************/
#define DBG_ENABLE
#define DBG_TAG "drv.pwm"
#define DBG_LEVEL DBG_LOG
#define DBG_COLOR
#include <rtdbg.h>
/********************************************************************************************************
* Private Type Declarations *
*******************************************************************************************************/
struct s32k14x_pwm
{
struct rt_device_pwm pwm_device;
rt_uint8_t instance;
ftm_user_config_t * user_cfg;
ftm_pwm_param_t * pwm_cfg;
ftm_state_t state;
char *name;
};
/********************************************************************************************************
* Private Variable Definitions *
*******************************************************************************************************/
static struct s32k14x_pwm ftm0_pwm5 =
{
.instance = INST_FLEXTIMER_PWM_1,
.user_cfg = &flexTimer_pwm_1_InitConfig,
.pwm_cfg = &flexTimer_pwm_1_PwmConfig,
.name = "pwm0"
};
static FTM_Type * const g_pwm_ftmBase[FTM_INSTANCE_COUNT] = FTM_BASE_PTRS;
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
};
/********************************************************************************************************
* Private Function Declarations *
*******************************************************************************************************/
static rt_err_t drv_pwm_enable(rt_uint32_t instance,struct rt_pwm_configuration *configuration, rt_bool_t enable)
{
status_t ret;
if (!configuration->complementary)
{
ret = FTM_DRV_ControlChannelOutput(instance, configuration->channel,enable);
}
return ret == STATUS_SUCCESS ? RT_EOK : RT_ERROR;
}
static rt_err_t drv_pwm_set_period(rt_uint32_t instance, struct rt_pwm_configuration *configuration)
{
status_t ret;
/* Convert nanosecond to frequency and duty cycle. 1s = 1 * 1000 * 1000 * 1000 ns */
ret = FTM_DRV_UpdatePwmPeriod(instance,FTM_PWM_UPDATE_IN_DUTY_CYCLE,1000000000/configuration->period,true);
return ret == STATUS_SUCCESS ? RT_EOK : RT_ERROR;
}
static rt_err_t drv_pwm_set_pulse(rt_uint32_t instanc, ftm_state_t *state,struct rt_pwm_configuration *configuration)
{
status_t ret;
uint16_t firstEdge;
uint16_t duty;
duty = (configuration->pulse*100)/(1000000000/state->ftmSourceClockFrequency);
firstEdge = (uint16_t)(327.68f*((float)duty));
ret = FTM_DRV_UpdatePwmChannel(INST_FLEXTIMER_PWM_1,configuration->channel,FTM_PWM_UPDATE_IN_DUTY_CYCLE,firstEdge,0U,true);
return ret == STATUS_SUCCESS ? RT_EOK : RT_ERROR;
}
static rt_err_t drv_pwm_set(rt_uint32_t instance, ftm_state_t *state ,struct rt_pwm_configuration *configuration)
{
status_t ret = STATUS_SUCCESS;
uint16_t firstEdge;
uint16_t duty;
/* Convert nanosecond to frequency and duty cycle. 1s = 1 * 1000 * 1000 * 1000 ns */
ret = FTM_DRV_UpdatePwmPeriod(instance,FTM_PWM_UPDATE_IN_DUTY_CYCLE,1000000000/configuration->period,true);
if(ret != STATUS_SUCCESS)
goto __exit;
duty = (configuration->pulse*100)/(configuration->period);
firstEdge = (uint16_t)(327.68f*((float)duty));
ret = FTM_DRV_UpdatePwmChannel(INST_FLEXTIMER_PWM_1,configuration->channel,FTM_PWM_UPDATE_IN_DUTY_CYCLE,firstEdge,0U,true);
__exit:
return ret == STATUS_SUCCESS ? RT_EOK : RT_ERROR;
}
static rt_err_t drv_pwm_get(rt_uint32_t instance,ftm_state_t *state, struct rt_pwm_configuration *configuration)
{
uint32_t mod = g_pwm_ftmBase[instance]->MOD + 1u;
uint32_t cval = g_pwm_ftmBase[instance]->CONTROLS[configuration->channel].CnV;
configuration->period = 1000000000/(state->ftmSourceClockFrequency/state->ftmPeriod);
configuration->pulse = configuration->period*cval/mod;
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;
rt_uint32_t instance = ((struct s32k14x_pwm *)(device->parent.user_data))->instance;
switch(cmd)
{
case PWM_CMD_ENABLE:
return drv_pwm_enable(instance,configuration,RT_TRUE);
case PWM_CMD_DISABLE:
return drv_pwm_enable(instance,configuration,RT_FALSE);
case PWM_CMD_SET:
return drv_pwm_set(instance,&((struct s32k14x_pwm *)(device->parent.user_data))->state,configuration);
case PWM_CMD_SET_PERIOD:
return drv_pwm_set_period(instance,configuration);
case PWM_CMD_SET_PULSE:
return drv_pwm_set_pulse(instance,&((struct s32k14x_pwm *)(device->parent.user_data))->state,configuration);
case PWM_CMD_GET:
return drv_pwm_get(instance,&((struct s32k14x_pwm *)(device->parent.user_data))->state,configuration);
default:
return -RT_EINVAL;
}
}
/********************************************************************************************************
* Global Function Declarations *
*******************************************************************************************************/
static int s32k14x_pwm_init(void)
{
status_t ret;
int result = RT_EOK;
/* config pwm timer */
ret = FTM_DRV_Init(ftm0_pwm5.instance, ftm0_pwm5.user_cfg, &ftm0_pwm5.state);
if(ret != STATUS_SUCCESS)
{
LOG_E("Ftm0 init failed %x.",ret);
result = -RT_ERROR;
goto __exit;
}
else
{
LOG_I("Ftm0 init OK.");
}
ret = FTM_DRV_InitPwm(ftm0_pwm5.instance, ftm0_pwm5.pwm_cfg);
if(ret != STATUS_SUCCESS)
{
LOG_E("Ftm0 pwm init failed %x.",ret);
result = -RT_ERROR;
goto __exit;
}
else
{
LOG_I("Ftm0 pwm init OK.");
}
if(rt_device_pwm_register(&ftm0_pwm5.pwm_device,ftm0_pwm5.name,&drv_ops,&ftm0_pwm5) == RT_EOK)
{
LOG_D("%s register success", ftm0_pwm5.name);
}
else
{
LOG_E("%s register failed", ftm0_pwm5.name);
result = -RT_ERROR;
}
__exit:
return result;
}
INIT_DEVICE_EXPORT(s32k14x_pwm_init);以上代码会注册设备到系统中,并会添加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], "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 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 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]);
#endif /* RT_USING_FINSH */
我们就可以使用pwm 命令来配置PWM 外设

【功能验证】
使用上述的pwm 命令来验证pwm 功能,首先使用pwm probe 命令来查找pwm0 设备

使用pwm set 命令来配置周期和脉冲宽度时长对应单位为ns

使用pwm get 来获取当前pwm 的信息

逻辑分析仪抓取波形和配置的参数保持一致

我要赚赏金
