204 lines
5.6 KiB
C
204 lines
5.6 KiB
C
/*
|
|
* Copyright (c) 2006-2023, RT-Thread Development Team
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Change Logs:
|
|
* Date Author Notes
|
|
* 2019-04-28 tyustli first version
|
|
* 2019-07-15 Magicoe The first version for LPC55S6x, we can also use SCT as PWM
|
|
*
|
|
*/
|
|
|
|
#include <rtthread.h>
|
|
|
|
#ifdef RT_USING_PWM
|
|
#if !defined(BSP_USING_CTIMER2_MAT0) && !defined(BSP_USING_CTIMER2_MAT1) && \
|
|
!defined(BSP_USING_CTIMER2_MAT2)
|
|
#error "Please define at least one BSP_USING_CTIMERx_MATx"
|
|
#else
|
|
#define BSP_USING_CTIMER2
|
|
#endif
|
|
|
|
#define LOG_TAG "drv.pwm"
|
|
#include <drv_log.h>
|
|
|
|
#include <rtdevice.h>
|
|
#include "fsl_ctimer.h"
|
|
#include "drv_pwm.h"
|
|
|
|
#define DEFAULT_DUTY 50
|
|
#define DEFAULT_FREQ 1000
|
|
|
|
static rt_err_t lpc_drv_pwm_control(struct rt_device_pwm *device, int cmd, void *arg);
|
|
|
|
static struct rt_pwm_ops lpc_drv_ops =
|
|
{
|
|
.control = lpc_drv_pwm_control
|
|
};
|
|
|
|
static rt_err_t lpc_drv_pwm_enable(struct rt_device_pwm *device, struct rt_pwm_configuration *configuration, rt_bool_t enable)
|
|
{
|
|
CTIMER_Type *base;
|
|
|
|
base = (CTIMER_Type *)device->parent.user_data;
|
|
|
|
if (!enable)
|
|
{
|
|
/* Stop the timer */
|
|
CTIMER_StopTimer(base);
|
|
}
|
|
else
|
|
{
|
|
/* Start the timer */
|
|
CTIMER_StartTimer(base);
|
|
}
|
|
|
|
return RT_EOK;
|
|
}
|
|
|
|
static rt_err_t lpc_drv_pwm_get(struct rt_device_pwm *device, struct rt_pwm_configuration *configuration)
|
|
{
|
|
uint8_t get_duty;
|
|
uint32_t get_frequence;
|
|
uint32_t pwmClock = 0;
|
|
CTIMER_Type *base;
|
|
|
|
base = (CTIMER_Type *)device->parent.user_data;
|
|
|
|
#ifdef BSP_USING_CTIMER2
|
|
/* get frequence */
|
|
pwmClock = CLOCK_GetCTimerClkFreq(2U) ;
|
|
#endif
|
|
|
|
get_frequence = pwmClock / (base->MR[kCTIMER_Match_3] + 1);
|
|
|
|
if(configuration->channel == 1)
|
|
{
|
|
/* get dutycycle */
|
|
get_duty = (100*(base->MR[kCTIMER_Match_3] + 1 - base->MR[kCTIMER_Match_1]))/(base->MR[kCTIMER_Match_3] + 1);
|
|
}
|
|
|
|
/* get dutycycle */
|
|
/* conversion */
|
|
configuration->period = 1000000000 / get_frequence;
|
|
configuration->pulse = get_duty * configuration->period / 100;
|
|
|
|
rt_kprintf("*** PWM period %d, pulse %d\r\n", configuration->period, configuration->pulse);
|
|
|
|
return RT_EOK;
|
|
}
|
|
|
|
static rt_err_t lpc_drv_pwm_set(struct rt_device_pwm *device, struct rt_pwm_configuration *configuration)
|
|
{
|
|
RT_ASSERT(configuration->period > 0);
|
|
RT_ASSERT(configuration->pulse <= configuration->period);
|
|
|
|
ctimer_config_t config;
|
|
CTIMER_Type *base;
|
|
base = (CTIMER_Type *)device->parent.user_data;
|
|
|
|
uint32_t pwmPeriod, pulsePeriod;
|
|
/* Run as a timer */
|
|
config.mode = kCTIMER_TimerMode;
|
|
/* This field is ignored when mode is timer */
|
|
config.input = kCTIMER_Capture_0;
|
|
/* Timer counter is incremented on every APB bus clock */
|
|
config.prescale = 0;
|
|
|
|
if(configuration->channel == 1)
|
|
{
|
|
/* Get the PWM period match value and pulse width match value of DEFAULT_FREQ PWM signal with DEFAULT_DUTY dutycycle */
|
|
/* Calculate PWM period match value */
|
|
double tmp = configuration->period;
|
|
/* Target frequence. */
|
|
tmp = 1000000000/tmp;
|
|
pwmPeriod = (( CLOCK_GetCTimerClkFreq(2U) / (config.prescale + 1) ) / (uint32_t)tmp) - 1;
|
|
|
|
/* Calculate pulse width match value */
|
|
tmp = configuration->pulse;
|
|
pulsePeriod = (1.0 - tmp / configuration->period) * pwmPeriod;
|
|
/* Match on channel 3 will define the PWM period */
|
|
base->MR[kCTIMER_Match_3] = pwmPeriod;
|
|
/* This will define the PWM pulse period */
|
|
base->MR[kCTIMER_Match_1] = pulsePeriod;
|
|
|
|
}
|
|
|
|
return RT_EOK;
|
|
}
|
|
|
|
static rt_err_t lpc_drv_pwm_control(struct rt_device_pwm *device, int cmd, void *arg)
|
|
{
|
|
struct rt_pwm_configuration *configuration = (struct rt_pwm_configuration *)arg;
|
|
|
|
switch (cmd)
|
|
{
|
|
case PWM_CMD_ENABLE:
|
|
return lpc_drv_pwm_enable(device, configuration, RT_TRUE);
|
|
case PWM_CMD_DISABLE:
|
|
return lpc_drv_pwm_enable(device, configuration, RT_FALSE);
|
|
case PWM_CMD_SET:
|
|
return lpc_drv_pwm_set(device, configuration);
|
|
case PWM_CMD_GET:
|
|
return lpc_drv_pwm_get(device, configuration);
|
|
default:
|
|
return RT_EINVAL;
|
|
}
|
|
}
|
|
|
|
int rt_hw_pwm_init(void)
|
|
{
|
|
rt_err_t ret = RT_EOK;
|
|
|
|
#ifdef BSP_USING_CTIMER2
|
|
|
|
static struct rt_device_pwm pwm1_device;
|
|
ctimer_config_t config;
|
|
uint32_t pwmPeriod, pulsePeriod;
|
|
|
|
/* Use 12 MHz clock for some of the Ctimers */
|
|
CLOCK_AttachClk(kMAIN_CLK_to_CTIMER2);
|
|
|
|
/* Run as a timer */
|
|
config.mode = kCTIMER_TimerMode;
|
|
/* This field is ignored when mode is timer */
|
|
config.input = kCTIMER_Capture_0;
|
|
/* Timer counter is incremented on every APB bus clock */
|
|
config.prescale = 0;
|
|
|
|
CTIMER_Init(CTIMER2, &config);
|
|
|
|
#ifdef BSP_USING_CTIMER2_MAT1
|
|
/* Get the PWM period match value and pulse width match value of DEFAULT_FREQ PWM signal with DEFAULT_DUTY dutycycle */
|
|
/* Calculate PWM period match value */
|
|
pwmPeriod = (( CLOCK_GetCTimerClkFreq(2U) / (config.prescale + 1) ) / DEFAULT_FREQ) - 1;
|
|
|
|
/* Calculate pulse width match value */
|
|
if (DEFAULT_DUTY == 0)
|
|
{
|
|
pulsePeriod = pwmPeriod + 1;
|
|
}
|
|
else
|
|
{
|
|
pulsePeriod = ((pwmPeriod + 1) * (100 - DEFAULT_DUTY)) / 100;
|
|
}
|
|
CTIMER_SetupPwmPeriod(CTIMER2, kCTIMER_Match_3 , kCTIMER_Match_1, pwmPeriod, pulsePeriod, false);
|
|
#endif
|
|
|
|
ret = rt_device_pwm_register(&pwm1_device, "pwm1", &lpc_drv_ops, CTIMER2);
|
|
|
|
if (ret != RT_EOK)
|
|
{
|
|
LOG_E("%s register failed", "pwm1");
|
|
}
|
|
|
|
#endif /* BSP_USING_CTIMER2 */
|
|
|
|
return ret;
|
|
}
|
|
|
|
INIT_DEVICE_EXPORT(rt_hw_pwm_init);
|
|
|
|
#endif /* RT_USING_PWM */
|