rt-thread/bsp/apm32/libraries/Drivers/drv_pwm.c

485 lines
11 KiB
C

/*
* Copyright (c) 2006-2022, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-03-04 stevetong459 first version
*/
#include <board.h>
#ifdef RT_USING_PWM
#include <drivers/rt_drv_pwm.h>
#define LOG_TAG "drv.pwm"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#define MAX_PERIOD 65535
#define MIN_PERIOD 3
#define MIN_PULSE 2
#define _PWM_GPIO_INIT(port_num, pin_num) \
do \
{ \
RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_GPIO##port_num); \
gpio_config->pin = GPIO_PIN_##pin_num; \
gpio_config->mode = GPIO_MODE_AF_PP; \
gpio_config->speed = GPIO_SPEED_50MHz; \
GPIO_Config(GPIO##port_num, gpio_config); \
} while (0)
enum
{
#ifdef BSP_USING_PWM1
PWM1_INDEX,
#endif
#ifdef BSP_USING_PWM2
PWM2_INDEX,
#endif
#ifdef BSP_USING_PWM3
PWM3_INDEX,
#endif
#ifdef BSP_USING_PWM4
PWM4_INDEX,
#endif
#ifdef BSP_USING_PWM5
PWM5_INDEX,
#endif
#ifdef BSP_USING_PWM8
PWM8_INDEX,
#endif
};
struct apm32_pwm
{
char *name;
TMR_T *tmr;
rt_uint8_t channel;
struct rt_device_pwm pwm_device;
};
static struct apm32_pwm pwm_config[] =
{
#ifdef BSP_USING_PWM1
{
"pwm1",
TMR1,
0,
},
#endif
#ifdef BSP_USING_PWM2
{
"pwm2",
TMR2,
0,
},
#endif
#ifdef BSP_USING_PWM3
{
"pwm3",
TMR3,
0,
},
#endif
#ifdef BSP_USING_PWM4
{
"pwm4",
TMR4,
0,
},
#endif
#ifdef BSP_USING_PWM5
{
"pwm5",
TMR5,
0,
},
#endif
#ifdef BSP_USING_PWM8
{
"pwm8",
TMR8,
0,
},
#endif
};
static void _pwm_channel_init(GPIO_Config_T *gpio_config)
{
#ifdef BSP_USING_PWM1_CH1
pwm_config[PWM1_INDEX].channel |= 1 << 0;
_PWM_GPIO_INIT(A, 8);
#endif
#ifdef BSP_USING_PWM1_CH2
pwm_config[PWM1_INDEX].channel |= 1 << 1;
_PWM_GPIO_INIT(A, 9);
#endif
#ifdef BSP_USING_PWM1_CH3
pwm_config[PWM1_INDEX].channel |= 1 << 2;
_PWM_GPIO_INIT(A, 10);
#endif
#ifdef BSP_USING_PWM1_CH4
pwm_config[PWM1_INDEX].channel |= 1 << 3;
_PWM_GPIO_INIT(A, 11);
#endif
#ifdef BSP_USING_PWM2_CH1
pwm_config[PWM2_INDEX].channel |= 1 << 0;
_PWM_GPIO_INIT(A, 0);
#endif
#ifdef BSP_USING_PWM2_CH2
pwm_config[PWM2_INDEX].channel |= 1 << 1;
_PWM_GPIO_INIT(A, 1);
#endif
#ifdef BSP_USING_PWM2_CH3
pwm_config[PWM2_INDEX].channel |= 1 << 2;
_PWM_GPIO_INIT(A, 2);
#endif
#ifdef BSP_USING_PWM2_CH4
pwm_config[PWM2_INDEX].channel |= 1 << 3;
_PWM_GPIO_INIT(A, 3);
#endif
#ifdef BSP_USING_PWM3_CH1
pwm_config[PWM3_INDEX].channel |= 1 << 0;
GPIO_ConfigPinRemap(GPIO_FULL_REMAP_TMR3);
_PWM_GPIO_INIT(C, 6);
#endif
#ifdef BSP_USING_PWM3_CH2
pwm_config[PWM3_INDEX].channel |= 1 << 1;
GPIO_ConfigPinRemap(GPIO_FULL_REMAP_TMR3);
_PWM_GPIO_INIT(C, 7);
#endif
#ifdef BSP_USING_PWM3_CH3
pwm_config[PWM3_INDEX].channel |= 1 << 2;
GPIO_ConfigPinRemap(GPIO_FULL_REMAP_TMR3);
_PWM_GPIO_INIT(C, 8);
#endif
#ifdef BSP_USING_PWM3_CH4
pwm_config[PWM3_INDEX].channel |= 1 << 3;
GPIO_ConfigPinRemap(GPIO_FULL_REMAP_TMR3);
_PWM_GPIO_INIT(C, 9);
#endif
#ifdef BSP_USING_PWM4_CH1
pwm_config[PWM4_INDEX].channel |= 1 << 0;
_PWM_GPIO_INIT(B, 6);
#endif
#ifdef BSP_USING_PWM4_CH2
pwm_config[PWM4_INDEX].channel |= 1 << 1;
_PWM_GPIO_INIT(B, 7);
#endif
#ifdef BSP_USING_PWM4_CH3
pwm_config[PWM4_INDEX].channel |= 1 << 2;
_PWM_GPIO_INIT(B, 8);
#endif
#ifdef BSP_USING_PWM4_CH4
pwm_config[PWM4_INDEX].channel |= 1 << 3;
_PWM_GPIO_INIT(B, 9);
#endif
#ifdef BSP_USING_PWM5_CH1
pwm_config[PWM5_INDEX].channel |= 1 << 0;
_PWM_GPIO_INIT(A, 0);
#endif
#ifdef BSP_USING_PWM5_CH2
pwm_config[PWM5_INDEX].channel |= 1 << 1;
_PWM_GPIO_INIT(A, 1);
#endif
#ifdef BSP_USING_PWM5_CH3
pwm_config[PWM5_INDEX].channel |= 1 << 2;
_PWM_GPIO_INIT(A, 2);
#endif
#ifdef BSP_USING_PWM5_CH4
pwm_config[PWM5_INDEX].channel |= 1 << 3;
_PWM_GPIO_INIT(A, 3);
#endif
#ifdef BSP_USING_PWM8_CH1
pwm_config[PWM8_INDEX].channel |= 1 << 0;
_PWM_GPIO_INIT(C, 6);
#endif
#ifdef BSP_USING_PWM8_CH2
pwm_config[PWM8_INDEX].channel |= 1 << 1;
_PWM_GPIO_INIT(C, 7);
#endif
#ifdef BSP_USING_PWM8_CH3
pwm_config[PWM8_INDEX].channel |= 1 << 2;
_PWM_GPIO_INIT(C, 8);
#endif
#ifdef BSP_USING_PWM8_CH4
pwm_config[PWM8_INDEX].channel |= 1 << 3;
_PWM_GPIO_INIT(C, 9);
#endif
}
static rt_err_t _pwm_hw_init(struct apm32_pwm *device)
{
rt_err_t result = RT_EOK;
TMR_T *tmr = RT_NULL;
TMR_BaseConfig_T base_config;
TMR_OCConfig_T oc_config;
RT_ASSERT(device != RT_NULL);
tmr = (TMR_T *)device->tmr;
if (tmr == TMR1)
{
RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_TMR1);
}
else if (tmr == TMR8)
{
RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_TMR8);
}
else if (tmr == TMR2)
{
RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_TMR2);
}
else if (tmr == TMR3)
{
RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_TMR3);
}
else if (tmr == TMR4)
{
RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_TMR4);
}
else if (tmr == TMR5)
{
RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_TMR5);
}
/* configure the tmrer to pwm mode */
base_config.division = 0;
base_config.countMode = TMR_COUNTER_MODE_UP;
base_config.period = 0;
base_config.clockDivision = TMR_CLOCK_DIV_1;
TMR_ConfigTimeBase(tmr, &base_config);
TMR_SelectOutputTrigger(tmr, TMR_TRGO_SOURCE_RESET);
TMR_DisableMasterSlaveMode(tmr);
oc_config.mode = TMR_OC_MODE_PWM1;
oc_config.pulse = 0;
oc_config.polarity = TMR_OC_POLARITY_HIGH;
oc_config.nIdleState = TMR_OC_NIDLE_STATE_RESET;
oc_config.idleState = TMR_OC_IDLE_STATE_RESET;
oc_config.outputState = TMR_OC_STATE_ENABLE;
/* config pwm channel */
if (device->channel & 0x01)
{
TMR_ConfigOC1(tmr, &oc_config);
}
if (device->channel & 0x02)
{
TMR_ConfigOC2(tmr, &oc_config);
}
if (device->channel & 0x04)
{
TMR_ConfigOC3(tmr, &oc_config);
}
if (device->channel & 0x08)
{
TMR_ConfigOC4(tmr, &oc_config);
}
/* enable update request source */
TMR_ConfigUpdateRequest(tmr, TMR_UPDATE_SOURCE_REGULAR);
return result;
}
static rt_uint32_t _pwm_timer_clock_get(TMR_T *tmr)
{
uint32_t pclk1;
RCM_ReadPCLKFreq(&pclk1, NULL);
return (rt_uint32_t)(pclk1 * ((RCM->CFG_B.APB1PSC != RCM_APB_DIV_1) ? 2 : 1));
}
static rt_err_t _pwm_enable(TMR_T *tmr, struct rt_pwm_configuration *configuration, rt_bool_t enable)
{
rt_uint32_t channel = (configuration->channel - 1) << 2;
if (enable)
{
if (configuration->complementary)
{
TMR_EnableCCxNChannel(tmr, (TMR_CHANNEL_T)(0x01 << (channel & 0x1FU)));
}
else
{
TMR_EnableCCxChannel(tmr, (TMR_CHANNEL_T)(0x01 << (channel & 0x1FU)));
}
if (tmr == TMR1 || tmr == TMR8)
{
TMR_EnablePWMOutputs(tmr);
}
TMR_Enable(tmr);
}
else
{
if (configuration->complementary)
{
TMR_DisableCCxNChannel(tmr, (TMR_CHANNEL_T)(0x01 << (channel & 0x1FU)));
}
else
{
TMR_DisableCCxChannel(tmr, (TMR_CHANNEL_T)(0x01 << (channel & 0x1FU)));
}
if (tmr == TMR1 || tmr == TMR8)
{
TMR_DisablePWMOutputs(tmr);
}
TMR_Disable(tmr);
}
return RT_EOK;
}
static rt_err_t _pwm_get(TMR_T *tmr, struct rt_pwm_configuration *configuration)
{
/* Converts the channel number to the channel number of library */
rt_uint32_t channel = (configuration->channel - 1) << 2;
rt_uint64_t timer_clock;
rt_uint32_t timer_reload, timer_psc;
timer_clock = _pwm_timer_clock_get(tmr);
if (tmr->CTRL1_B.CLKDIV == TMR_CLOCK_DIV_2)
{
timer_clock <<= 1;
}
else if (tmr->CTRL1_B.CLKDIV == TMR_CLOCK_DIV_4)
{
timer_clock <<= 2;
}
uint32_t temp;
temp = (uint32_t)tmr;
temp += (uint32_t)(0x34 + channel);
/* Convert nanosecond to frequency and duty cycle.*/
timer_clock /= 1000000UL;
timer_reload = tmr->AUTORLD;
timer_psc = tmr->PSC;
configuration->period = (timer_reload + 1) * (timer_psc + 1) * 1000UL / timer_clock;
configuration->pulse = ((*(__IO uint32_t *)temp) + 1) * (timer_psc + 1) * 1000UL / timer_clock;
return RT_EOK;
}
static rt_err_t _pwm_set(TMR_T *tmr, struct rt_pwm_configuration *configuration)
{
rt_uint32_t period, pulse;
rt_uint64_t timer_clock, psc;
rt_uint32_t channel = 0x04 * (configuration->channel - 1);
uint32_t temp = (uint32_t)tmr;
timer_clock = _pwm_timer_clock_get(tmr);
/* Convert nanosecond to frequency and duty cycle. */
timer_clock /= 1000000UL;
period = (unsigned long long)configuration->period * timer_clock / 1000ULL ;
psc = period / MAX_PERIOD + 1;
period = period / psc;
tmr->PSC = (uint16_t)(psc - 1);
if (period < MIN_PERIOD)
{
period = MIN_PERIOD;
}
tmr->AUTORLD = (uint16_t)(period - 1);
pulse = (unsigned long long)configuration->pulse * timer_clock / psc / 1000ULL;
if (pulse < MIN_PULSE)
{
pulse = MIN_PULSE;
}
else if (pulse > period)
{
pulse = period;
}
temp += (uint32_t)(0x34 + channel);
*(__IO uint32_t *)temp = pulse - 1;
tmr->CNT = 0;
/* Update frequency value */
TMR_GenerateEvent(tmr, TMR_EVENT_UPDATE);
return RT_EOK;
}
static rt_err_t _pwm_control(struct rt_device_pwm *device, int cmd, void *arg)
{
struct rt_pwm_configuration *configuration = (struct rt_pwm_configuration *)arg;
TMR_T *tmr = (TMR_T *)device->parent.user_data;
switch (cmd)
{
case PWMN_CMD_ENABLE:
configuration->complementary = RT_TRUE;
case PWM_CMD_ENABLE:
return _pwm_enable(tmr, configuration, RT_TRUE);
case PWMN_CMD_DISABLE:
configuration->complementary = RT_FALSE;
case PWM_CMD_DISABLE:
return _pwm_enable(tmr, configuration, RT_FALSE);
case PWM_CMD_SET:
return _pwm_set(tmr, configuration);
case PWM_CMD_GET:
return _pwm_get(tmr, configuration);
default:
return RT_EINVAL;
}
}
static const struct rt_pwm_ops _pwm_ops =
{
_pwm_control
};
static int rt_hw_pwm_init(void)
{
rt_uint32_t i = 0;
rt_err_t result = RT_EOK;
GPIO_Config_T gpio_config;
_pwm_channel_init(&gpio_config);
for (i = 0; i < sizeof(pwm_config) / sizeof(pwm_config[0]); i++)
{
/* pwm init */
if (_pwm_hw_init(&pwm_config[i]) != RT_EOK)
{
LOG_E("%s init failed", pwm_config[i].name);
return -RT_ERROR;
}
else
{
LOG_D("%s init success", pwm_config[i].name);
/* register pwm device */
if (rt_device_pwm_register(&pwm_config[i].pwm_device, pwm_config[i].name, &_pwm_ops, pwm_config[i].tmr) == RT_EOK)
{
LOG_D("%s register success", pwm_config[i].name);
}
else
{
LOG_E("%s register failed", pwm_config[i].name);
result = -RT_ERROR;
}
}
}
return result;
}
INIT_DEVICE_EXPORT(rt_hw_pwm_init);
#endif /* RT_USING_PWM */