kendryte-freertos-sdk/lib/bsp/device/pwm.c

99 lines
3.3 KiB
C

/* Copyright 2018 Canaan Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <FreeRTOS.h>
#include <driver.h>
#include <fpioa.h>
#include <hal.h>
#include <plic.h>
#include <semphr.h>
#include <stdio.h>
#include <sysctl.h>
#include <timer.h>
#include "fpioa_cfg.h"
#define COMMON_ENTRY \
pwm_data* data = (pwm_data*)userdata; \
volatile struct timer_t* pwm = (volatile struct timer_t*)data->base_addr; \
(void)pwm; \
(void)data;
typedef struct
{
enum sysctl_clock_e clock;
uintptr_t base_addr;
size_t pin_count;
struct
{
uint32_t periods;
};
} pwm_data;
static void pwm_install(void* userdata)
{
COMMON_ENTRY;
}
static int pwm_open(void* userdata)
{
return 1;
}
static void pwm_close(void* userdata)
{
}
static double pwm_set_frequency(double frequency, void* userdata)
{
COMMON_ENTRY;
uint32_t clk_freq = sysctl_clock_get_freq(data->clock);
/* frequency = clk_freq / periods */
int32_t periods = (int32_t)(clk_freq / frequency);
configASSERT(periods > 0 && periods <= INT32_MAX);
frequency = clk_freq / (double)periods;
data->periods = periods;
return frequency;
}
static double pwm_set_active_duty_cycle_percentage(size_t pin, double duty_cycle_percentage, void* userdata)
{
COMMON_ENTRY;
configASSERT(pin < data->pin_count);
configASSERT(duty_cycle_percentage >= 0 && duty_cycle_percentage <= 1);
uint32_t percent = (uint32_t)(duty_cycle_percentage * data->periods);
pwm->channel[pin].load_count = data->periods - percent;
pwm->load_count2[pin] = percent;
return percent / 100.0;
}
static void pwm_set_enable(size_t pin, int enable, void* userdata)
{
COMMON_ENTRY;
configASSERT(pin < data->pin_count);
if (enable)
pwm->channel[pin].control = TIMER_CR_INTERRUPT_MASK | TIMER_CR_PWM_ENABLE | TIMER_CR_USER_MODE | TIMER_CR_ENABLE;
else
pwm->channel[pin].control = TIMER_CR_INTERRUPT_MASK;
}
static pwm_data dev0_data = {SYSCTL_CLOCK_TIMER0, TIMER0_BASE_ADDR, 4, {0}};
static pwm_data dev1_data = {SYSCTL_CLOCK_TIMER1, TIMER1_BASE_ADDR, 4, {0}};
static pwm_data dev2_data = {SYSCTL_CLOCK_TIMER2, TIMER2_BASE_ADDR, 4, {0}};
const pwm_driver_t g_pwm_driver_pwm0 = {{&dev0_data, pwm_install, pwm_open, pwm_close}, 4, pwm_set_frequency, pwm_set_active_duty_cycle_percentage, pwm_set_enable};
const pwm_driver_t g_pwm_driver_pwm1 = {{&dev1_data, pwm_install, pwm_open, pwm_close}, 4, pwm_set_frequency, pwm_set_active_duty_cycle_percentage, pwm_set_enable};
const pwm_driver_t g_pwm_driver_pwm2 = {{&dev2_data, pwm_install, pwm_open, pwm_close}, 4, pwm_set_frequency, pwm_set_active_duty_cycle_percentage, pwm_set_enable};