【NXP-MCXA153】PWM驱动移植

介绍

‌PWM(‌Pulse Width Modulation)‌,‌脉冲宽度调制,‌是一种数字量控制模拟量的技术,‌常用于电机驱动、显示屏背光控制、逆变控制等;NXP-MCXA153开发板上有多路CTimer定时器,可以用来生成PWM波形

移植流程

① 在board里边添加相应的外设:配置时钟分频、引脚功能等

② 添加相应的config开关、Kconfig开关,用以指示相应的外设开启与关闭(本质是通过宏定义或者条件编译的方式)

③ 根据SDK_2_14_2_FRDM-MCXA153提供的simple_pwm示例工程编写pwm总线驱动,需要实现几个关键的函数

  • mcx_pwm_init
  • mcx_drv_pwm_control
  • mcx_drv_pwm_enable
  • mcx_drv_pwm_disable

④ 添加相应的库文件依赖:fsl_ctimer.c

驱动文件

pin_mux.c

BOARD_InitPins函数里加入以下代码:配置CTIMER1外设为复位状态、设置P1_4引脚功能

#ifdef BSP_USING_PWM0
	ctimer_config_t config;
	CTIMER_Init(CTIMER1, &config);
    const port_pin_config_t port1_4_pin62_config = {/* Internal pull-up/down resistor is disabled */
                                                    kPORT_PullDisable,
                                                    /* Low internal pull resistor value is selected. */
                                                    kPORT_LowPullResistor,
                                                    /* Fast slew rate is configured */
                                                    kPORT_FastSlewRate,
                                                    /* Passive input filter is disabled */
                                                    kPORT_PassiveFilterDisable,
                                                    /* Open drain output is disabled */
                                                    kPORT_OpenDrainDisable,
                                                    /* Low drive strength is configured */
                                                    kPORT_LowDriveStrength,
                                                    /* Normal drive strength is configured */
                                                    kPORT_NormalDriveStrength,
                                                    /* Pin is configured as CT1_MAT2 */
                                                    kPORT_MuxAlt4,
                                                    /* Digital input enabled */
                                                    kPORT_InputBufferEnable,
                                                    /* Digital input is not inverted */
                                                    kPORT_InputNormal,
                                                    /* Pin Control Register fields [15:0] are not locked */
                                                    kPORT_UnlockRegister};
    /* PORT1_4 (pin 62) is configured as CT1_MAT2 */
    PORT_SetPinConfig(PORT1, 4U, &port1_4_pin62_config);
#endif

board/Kconfig

加入PWM0相关配置

menuconfig BSP_USING_PWM
        config BSP_USING_PWM
            bool "Enable PWM"
            select RT_USING_PWM
            default y
            if BSP_USING_PWM
                config BSP_USING_PWM0
                    bool "Enable PWM0 output"
                    default y
            endif

drv_pwm.c

pwm驱动层适配如下

/*
 * Copyright (c) 2006-2024, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2024-8-3       hywing       Initial version.
 */

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

#ifdef RT_USING_PWM

typedef struct
{
    struct rt_device_pwm pwm_device;
    CTIMER_Type *ct_instance;
	uint32_t timerClock;
	const ctimer_match_t pwmPeriodChannel;
    ctimer_match_t matchChannel;
	char *name;
} mcx_pwm_obj_t;

static mcx_pwm_obj_t mcx_pwm_list[]= 
{
#ifndef BSP_USING_PWM0
    {
        .ct_instance = CTIMER1,
        .timerClock = 0,
		.pwmPeriodChannel = kCTIMER_Match_3,
		.matchChannel = kCTIMER_Match_2,
        .name = "pwm0",
    }
#endif
};
volatile uint32_t g_pwmPeriod   = 0U;
volatile uint32_t g_pulsePeriod = 0U;

static rt_err_t mcx_drv_pwm_get(mcx_pwm_obj_t *pwm, struct rt_pwm_configuration *configuration)
{
    return RT_EOK;
}

status_t CTIMER_GetPwmPeriodValue(uint32_t pwmFreqHz, uint8_t dutyCyclePercent, uint32_t timerClock_Hz)
{
    g_pwmPeriod = (timerClock_Hz / pwmFreqHz) - 1U;
    g_pulsePeriod = (g_pwmPeriod + 1U) * (100 - dutyCyclePercent) / 100;
    return kStatus_Success;
}

static rt_err_t mcx_drv_pwm_set(mcx_pwm_obj_t *pwm, struct rt_pwm_configuration *configuration)
{
    CTIMER_Type *ct = pwm->ct_instance;
	uint32_t pwmFreqHz = 1000000000 / configuration->period;
	uint8_t dutyCyclePercent = configuration->pulse * 100 / configuration->period; 
	CTIMER_GetPwmPeriodValue(pwmFreqHz, dutyCyclePercent, pwm->timerClock);
    CTIMER_SetupPwmPeriod(ct, kCTIMER_Match_3, kCTIMER_Match_2, g_pwmPeriod, g_pulsePeriod, false);
    return 0;
}

static rt_err_t mcx_drv_pwm_enable(mcx_pwm_obj_t *pwm, struct rt_pwm_configuration *configuration)
{
    CTIMER_StartTimer(pwm->ct_instance);
    return 0;
}

static rt_err_t mcx_drv_pwm_disable(mcx_pwm_obj_t *pwm, struct rt_pwm_configuration *configuration)
{
    CTIMER_StopTimer(pwm->ct_instance);
    return 0;
}

static rt_err_t mcx_drv_pwm_control(struct rt_device_pwm *device, int cmd, void *args)
{
    mcx_pwm_obj_t *pwm = device->parent.user_data;
    struct rt_pwm_configuration *configuration = (struct rt_pwm_configuration *)args;

    switch (cmd)
    {
    case PWM_CMD_ENABLE:
        return mcx_drv_pwm_enable(pwm, configuration);

    case PWM_CMD_DISABLE:
        return mcx_drv_pwm_disable(pwm, configuration);

    case PWM_CMD_SET:
        return mcx_drv_pwm_set(pwm, configuration);

    case PWM_CMD_GET:
        return mcx_drv_pwm_get(pwm, configuration);

    default:
        return -RT_EINVAL;
    }

    return RT_EOK;
}

static struct rt_pwm_ops mcx_pwm_ops =
{
    .control = mcx_drv_pwm_control,
};

int mcx_pwm_init(void)
{
    rt_err_t ret;
    char name_buf[8];
	
	ctimer_config_t config;
	CTIMER_GetDefaultConfig(&config);
    for (uint8_t i = 0; i < ARRAY_SIZE(mcx_pwm_list); i++)
    {
        mcx_pwm_list[i].timerClock = CLOCK_GetCTimerClkFreq(1U) / (config.prescale + 1);
		CTIMER_Init(mcx_pwm_list[i].ct_instance, &config);
        ret = rt_device_pwm_register(&mcx_pwm_list[i].pwm_device, mcx_pwm_list[i].name, &mcx_pwm_ops, &mcx_pwm_list[i]);
        if (ret != RT_EOK)
        {
            return ret;
        }
    }
    return RT_EOK;
}

INIT_DEVICE_EXPORT(mcx_pwm_init);

#endif /* RT_USING_PWM */

SConscript

Libraries/MCXA153/SConscript文件里边加上以下代码

src += ['MCXA153/drivers/fsl_ctimer.c']

测试用例

打开使能pwm0驱动,保存配置推出menuconfig
在这里插入图片描述

导出MDK5工程

scons --target=mdk5

生成频率1KHz,占空比为5%的方波,最后编译并烧录到开发板上去

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

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

#define PWM_LED_DEV     "pwm0"
#define PWM_CHANNEL     3

int main(void)
{
    struct rt_device_pwm *pwm_dev = RT_NULL;
    rt_uint32_t period, pulse;
    period = 1000000;
    pulse = 50000;

    pwm_dev = (struct rt_device_pwm *)rt_device_find(PWM_LED_DEV);
    if (pwm_dev == RT_NULL)
    {
        rt_kprintf("pwm device (%s) not found!\n", PWM_LED_DEV);
        return RT_ERROR;
    }

    rt_pwm_enable(pwm_dev, PWM_CHANNEL);
	rt_pwm_set(pwm_dev, PWM_CHANNEL, period, pulse);
    while (1)
    {
    	rt_thread_mdelay(1000);
    }
    return 0;
}

实验效果

使用逻辑分析仪测量P1_4生成的波形,波形刚好频率为1KHz,占空比为5%
在这里插入图片描述

总结

其实这个是用NXP定时器配置的PWM,并不是原生的PWM,但也能满足要求

  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

下里巴人hywing

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值