pwm.c 5.83 KB
/*
 * Copyright (C) 2015 Freie Universität Berlin
 *
 * This file is subject to the terms and conditions of the GNU Lesser General
 * Public License v2.1. See the file LICENSE in the top level directory for more
 * details.
 */

/**
 * @ingroup     lpc11u34
 * @{
 *
 * @file
 * @brief       CPU specific low-level PWM driver implementation for LPC11U34
 *
 * @author      Paul RATHGEB <paul.rathgeb@skynet.be>
 *
 * @}
 */

#include "bitarithm.h"
#include "periph/gpio.h"
#include "board.h"
#include "periph_conf.h"

/* guard file in case no PWM device is defined */
#include "periph/pwm.h"
#if (PWM_0_EN || PWM_1_EN)

/**
 * @note    The LPC11U34 doesn't support centerized alignements
 */
uint32_t pwm_init(pwm_t dev, pwm_mode_t mode, uint32_t freq, uint16_t res)
{
    switch (dev) {
#if PWM_0_EN
        case PWM_0:
            /* This CPU doesn't support a centerized alignement */
            if (mode == PWM_CENTER) {
                return 0;
            }
            /* Check if the frequency and resolution is applicable */
            if (CLOCK_CORECLOCK / (res * freq) <= 0) {
                return 0;
            }
#if PWM_0_CH0_EN
            PWM_0_CH0_IOCON = (PWM_0_CH0_IOCON & ~(BIT7 | 7)) | PWM_0_CH0_AF;
#endif
#if PWM_0_CH1_EN
            PWM_0_CH1_IOCON = (PWM_0_CH1_IOCON & ~(BIT7 | 7)) | PWM_0_CH1_AF;
#endif
#if PWM_0_CH2_EN
            PWM_0_CH2_IOCON = (PWM_0_CH2_IOCON & ~(BIT7 | 7)) | PWM_0_CH2_AF;
#endif
            /* The configuration involve that the peripheral is powered */
            pwm_poweron(dev);
            /* Enable timer and keep it in reset state */
            PWM_0_DEV->TCR = BIT0 | BIT1;
            /* Set the prescaler (CLOCK_CORECLOCK / resolution) */
            PWM_0_DEV->PR = (CLOCK_CORECLOCK / (res * freq));
            /* Reset timer on MR3 */
            PWM_0_DEV->MCR = BIT10;

            /* Set PWM period */
            PWM_0_DEV->MR0 = res;
            PWM_0_DEV->MR1 = res;
            PWM_0_DEV->MR2 = res;
            PWM_0_DEV->MR3 = res - 1;

            /* Set mode for channels 0..2 */
            PWM_0_DEV->EMR |= ((mode + 1) << 4);
            PWM_0_DEV->EMR |= ((mode + 1) << 6);
            PWM_0_DEV->EMR |= ((mode + 1) << 8);

            /* Enable PWM channels 0..2 */
            PWM_0_DEV->PWMC = BIT0 | BIT1 | BIT2;
#endif /* PWM_0_EN */
#if PWM_1_EN
        case PWM_1:
            /* This CPU doesn't support a centerized alignement */
            if (mode == PWM_CENTER) {
                return 0;
            }
            /* Check if the frequency and resolution is applicable */
            if (CLOCK_CORECLOCK / (res * freq) <= 0) {
                return 0;
            }
#if PWM_1_CH0_EN
            PWM_1_CH0_IOCON = (PWM_1_CH0_IOCON & ~(BIT7 | 7)) | PWM_1_CH0_AF;
#endif
#if PWM_1_CH1_EN
            PWM_1_CH1_IOCON = (PWM_1_CH1_IOCON & ~(BIT7 | 7)) | PWM_1_CH1_AF;
#endif
#if PWM_1_CH2_EN
            PWM_1_CH2_IOCON = (PWM_1_CH2_IOCON & ~(BIT7 | 7)) | PWM_1_CH2_AF;
#endif
            /* The configuration involve that the peripheral is powered */
            pwm_poweron(dev);
            /* Enable timer and keep it in reset state */
            PWM_1_DEV->TCR = BIT0 | BIT1;
            /* Set the prescaler (CLOCK_CORECLOCK / resolution) */
            PWM_1_DEV->PR = (CLOCK_CORECLOCK / (res * freq));
            /* Reset timer on MR3 */
            PWM_1_DEV->MCR = BIT10;

            /* Set PWM period */
            PWM_1_DEV->MR0 = res;
            PWM_1_DEV->MR1 = res;
            PWM_1_DEV->MR2 = res;
            PWM_1_DEV->MR3 = res - 1;

            /* Set mode for channels 0..2 */
            PWM_1_DEV->EMR |= ((mode + 1) << 4);
            PWM_1_DEV->EMR |= ((mode + 1) << 6);
            PWM_1_DEV->EMR |= ((mode + 1) << 8);

            /* Enable PWM channels 0..2 */
            PWM_1_DEV->PWMC = BIT0 | BIT1 | BIT2;
#endif /* PWM_1_EN */
    }

    return freq;
}

uint8_t pwm_channels(pwm_t dev)
{
    switch (dev) {
#if PWM_0_EN
        case PWM_0:
            return PWM_0_CHANNELS;
#endif
#if PWM_1_EN
        case PWM_1:
            return PWM_1_CHANNELS;
#endif
        default:
            return 0;
    }
}

void pwm_set(pwm_t dev, uint8_t channel, uint16_t value)
{
    switch (dev) {
#if PWM_0_EN
        case PWM_0:
            if (channel <= 2) {
                PWM_0_DEV->MR[channel] = PWM_0_DEV->MR3 - value;
            }
            break;
#endif
#if PWM_1_EN
        case PWM_1:
            if (channel <= 2) {
                PWM_1_DEV->MR[channel] = PWM_1_DEV->MR3 - value;
            }
            break;
#endif
    }
}

void pwm_start(pwm_t dev)
{
    switch (dev) {
#if PWM_0_EN
        case PWM_0:
            /* Start the counter */
            PWM_0_DEV->TCR &= ~BIT1;
            break;
#endif
#if PWM_1_EN
        case PWM_1:
            /* Start the counter */
            PWM_1_DEV->TCR &= ~BIT1;
            break;
#endif
    }
}

void pwm_stop(pwm_t dev)
{
    switch (dev) {
#if PWM_0_EN
        case PWM_0:
            /* Stop the counter */
            PWM_0_DEV->TCR |= BIT1;
            break;
#endif
#if PWM_1_EN
        case PWM_1:
            /* Stop the counter */
            PWM_1_DEV->TCR |= BIT1;
            break;
#endif
    }
}

void pwm_poweron(pwm_t dev)
{
    switch (dev) {
#if PWM_0_EN
        case PWM_0:
            /* Enable clock for PWM_0 */
            LPC_SYSCON->SYSAHBCLKCTRL |= PWM_0_CLK;
            break;
#endif
#if PWM_1_EN
        case PWM_1:
            /* Enable clock for PWM_1 */
            LPC_SYSCON->SYSAHBCLKCTRL |= PWM_1_CLK;
            break;
#endif
    }
}

void pwm_poweroff(pwm_t dev)
{
    switch (dev) {
#if PWM_0_EN
        case PWM_0:
            /* Disable clock for PWM_0 */
            LPC_SYSCON->SYSAHBCLKCTRL &= ~PWM_0_CLK;
            break;
#endif
#if PWM_1_EN
        case PWM_1:
            /* Disable clock for PWM_1 */
            LPC_SYSCON->SYSAHBCLKCTRL &= ~PWM_1_CLK;
            break;
#endif
    }
}
#endif /* (PWM_0_EN || PWM_1_EN) */