2018-01-22 12:16:19 +00:00
|
|
|
/* mbed Microcontroller Library
|
|
|
|
* Copyright (c) 2006-2013 ARM Limited
|
|
|
|
*
|
|
|
|
* 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 "pwmout_api.h"
|
|
|
|
#include "PeripheralNames.h"
|
|
|
|
#include "pinmap.h"
|
|
|
|
#include "tmpm46b_tmrb.h"
|
|
|
|
|
|
|
|
#define MAX_COUNTER_16B 0xFFFF
|
|
|
|
|
|
|
|
static TMRB_InitTypeDef m_tmrb;
|
|
|
|
static TMRB_FFOutputTypeDef FFStruct;
|
|
|
|
|
|
|
|
static const PinMap PinMap_PWM[] = {
|
|
|
|
{PE4, PWM_0, PIN_DATA(5, 1)},
|
|
|
|
{PB6, PWM_1, PIN_DATA(4, 1)},
|
|
|
|
{PH1, PWM_2, PIN_DATA(2, 1)},
|
|
|
|
{PH0, PWM_3, PIN_DATA(2, 1)},
|
|
|
|
{PK1, PWM_4, PIN_DATA(4, 1)},
|
|
|
|
{PA7, PWM_5, PIN_DATA(5, 1)},
|
|
|
|
{NC, NC, 0}
|
|
|
|
};
|
|
|
|
|
|
|
|
static const uint32_t prescale_tbl[] = {
|
|
|
|
2, 8, 32, 64, 128, 256, 512
|
|
|
|
};
|
|
|
|
|
2018-09-25 07:59:12 +00:00
|
|
|
#define CLOCK_FREQUENCY (SystemCoreClock) // Input source clock
|
2018-01-22 12:16:19 +00:00
|
|
|
|
|
|
|
void pwmout_init(pwmout_t *obj, PinName pin)
|
|
|
|
{
|
|
|
|
// Determine the pwm channel
|
|
|
|
PWMName pwm = (PWMName)pinmap_peripheral(pin, PinMap_PWM);
|
|
|
|
//Assert input is valid
|
|
|
|
MBED_ASSERT(pwm != (PWMName)NC);
|
|
|
|
switch (pwm) {
|
|
|
|
case PWM_0:
|
|
|
|
obj->channel = TSB_TB2;
|
|
|
|
break;
|
|
|
|
case PWM_1:
|
|
|
|
obj->channel = TSB_TB3;
|
|
|
|
break;
|
|
|
|
case PWM_2:
|
|
|
|
obj->channel = TSB_TB4;
|
|
|
|
break;
|
|
|
|
case PWM_3:
|
|
|
|
obj->channel = TSB_TB5;
|
|
|
|
break;
|
|
|
|
case PWM_4:
|
|
|
|
obj->channel = TSB_TB6;
|
|
|
|
break;
|
|
|
|
case PWM_5:
|
|
|
|
obj->channel = TSB_TB7;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
obj->channel = NULL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
CG_SetFcPeriphA((0x01U << (15U + pwm)), ENABLE);
|
|
|
|
TMRB_SetIdleMode(TSB_TB0, DISABLE);
|
|
|
|
// Set pin function as PWM
|
|
|
|
pinmap_pinout(pin, PinMap_PWM);
|
|
|
|
// Default to 20ms, 0% duty cycle
|
|
|
|
pwmout_period_ms(obj, 20);
|
|
|
|
}
|
|
|
|
|
|
|
|
void pwmout_free(pwmout_t *obj)
|
|
|
|
{
|
|
|
|
// Stops and clear count operation
|
|
|
|
TMRB_SetRunState(obj->channel, TMRB_STOP);
|
|
|
|
pwmout_write(obj,0);
|
|
|
|
obj->channel = NULL;
|
|
|
|
obj->trailing_timing = 0;
|
|
|
|
obj->leading_timing = 0;
|
|
|
|
obj->divisor = 0;
|
|
|
|
TMRB_SetIdleMode(TSB_TB0, ENABLE);
|
|
|
|
}
|
|
|
|
|
|
|
|
void pwmout_write(pwmout_t *obj, float value)
|
|
|
|
{
|
|
|
|
// Stop timer for setting clock again
|
|
|
|
TMRB_SetRunState(obj->channel, TMRB_STOP);
|
|
|
|
// values outside this range will be saturated to 0.0f or 1.0f
|
|
|
|
// Disable flip-flop reverse trigger when leading_timing and trailing_timing are duplicated
|
|
|
|
if (value <= 0.0f) {
|
|
|
|
value = 0;
|
|
|
|
FFStruct.FlipflopCtrl = TMRB_FLIPFLOP_CLEAR;
|
|
|
|
FFStruct.FlipflopReverseTrg = TMRB_DISABLE_FLIPFLOP;
|
|
|
|
} else if (value >= 1.0f) {
|
|
|
|
value = 1;
|
|
|
|
FFStruct.FlipflopCtrl = TMRB_FLIPFLOP_SET;
|
|
|
|
FFStruct.FlipflopReverseTrg = TMRB_DISABLE_FLIPFLOP;
|
|
|
|
} else {
|
|
|
|
FFStruct.FlipflopCtrl = TMRB_FLIPFLOP_CLEAR;
|
|
|
|
FFStruct.FlipflopReverseTrg = (TMRB_FLIPFLOP_MATCH_TRAILING | TMRB_FLIPFLOP_MATCH_LEADING);
|
|
|
|
}
|
|
|
|
TMRB_SetFlipFlop(obj->channel, &FFStruct);
|
|
|
|
|
2018-09-25 07:59:12 +00:00
|
|
|
if (obj->period > 0.560) {
|
|
|
|
value = 1; // TMPM46B duty cycle should be < 560ms, above 560ms fixed 50% duty cycle
|
2018-01-22 12:16:19 +00:00
|
|
|
}
|
|
|
|
// Store the new leading_timing value
|
|
|
|
obj->leading_timing = obj->trailing_timing - (uint16_t)(obj->trailing_timing * value);
|
|
|
|
|
|
|
|
// Setting TBxRG0 register
|
|
|
|
TMRB_ChangeLeadingTiming(obj->channel, obj->leading_timing);
|
|
|
|
TMRB_SetRunState(obj->channel, TMRB_RUN);
|
|
|
|
}
|
|
|
|
|
|
|
|
float pwmout_read(pwmout_t *obj)
|
|
|
|
{
|
|
|
|
float duty_cycle = (float)(obj->trailing_timing - obj->leading_timing) / obj->trailing_timing;
|
|
|
|
return duty_cycle;
|
|
|
|
}
|
|
|
|
|
|
|
|
void pwmout_period(pwmout_t *obj, float seconds)
|
|
|
|
{
|
|
|
|
pwmout_period_us(obj, (int)(seconds * 1000000.0f));
|
|
|
|
}
|
|
|
|
|
|
|
|
void pwmout_period_ms(pwmout_t *obj, int ms)
|
|
|
|
{
|
|
|
|
pwmout_period_us(obj, (ms * 1000));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set the PWM period, keeping the duty cycle the same.
|
|
|
|
void pwmout_period_us(pwmout_t *obj, int us)
|
|
|
|
{
|
|
|
|
float seconds = 0;
|
|
|
|
uint32_t cycles = 0;
|
|
|
|
int ClkDiv = 0;
|
|
|
|
int i = 0;
|
|
|
|
float duty_cycle = 0;
|
|
|
|
uint32_t clk_freq = 0;
|
|
|
|
|
|
|
|
seconds = (float)((us) / 1000000.0f);
|
|
|
|
obj->period = seconds;
|
|
|
|
|
2018-09-25 07:59:12 +00:00
|
|
|
if (obj->period > 0.560) {
|
2018-01-22 12:16:19 +00:00
|
|
|
clk_freq = (CLOCK_FREQUENCY / 2);
|
|
|
|
} else {
|
|
|
|
clk_freq = CLOCK_FREQUENCY;
|
|
|
|
}
|
|
|
|
// Select highest timer resolution
|
|
|
|
for (i = 0; i < 7; ++i) {
|
|
|
|
cycles = (int)((clk_freq / prescale_tbl[i]) * seconds);
|
|
|
|
if (cycles <= MAX_COUNTER_16B) {
|
|
|
|
ClkDiv = i + 1; // range 1:6
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
cycles = MAX_COUNTER_16B;
|
|
|
|
ClkDiv = 7;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Stops and clear count operation
|
|
|
|
TMRB_SetRunState(obj->channel, TMRB_STOP);
|
|
|
|
// Restore the duty-cycle
|
|
|
|
duty_cycle = (float)((obj->trailing_timing - obj->leading_timing) / obj->trailing_timing);
|
|
|
|
obj->trailing_timing = cycles;
|
|
|
|
obj->leading_timing = ((cycles)- (uint16_t)(cycles * duty_cycle));
|
|
|
|
|
|
|
|
// Change the source clock division and period
|
|
|
|
m_tmrb.Mode = TMRB_INTERVAL_TIMER;
|
|
|
|
m_tmrb.ClkDiv = ClkDiv;
|
|
|
|
m_tmrb.UpCntCtrl = TMRB_AUTO_CLEAR;
|
|
|
|
m_tmrb.TrailingTiming = obj->trailing_timing;
|
|
|
|
m_tmrb.LeadingTiming = obj->leading_timing;
|
|
|
|
FFStruct.FlipflopCtrl = TMRB_FLIPFLOP_SET;
|
|
|
|
FFStruct.FlipflopReverseTrg = (TMRB_FLIPFLOP_MATCH_TRAILING | TMRB_FLIPFLOP_MATCH_LEADING);
|
|
|
|
// Enable channel
|
|
|
|
TMRB_Enable(obj->channel);
|
|
|
|
// Disable double buffering
|
|
|
|
TMRB_SetDoubleBuf(obj->channel, DISABLE, TMRB_WRITE_REG_SEPARATE);
|
|
|
|
// Init timer function
|
|
|
|
TMRB_Init(obj->channel, &m_tmrb);
|
|
|
|
// Enable double buffering
|
|
|
|
TMRB_SetDoubleBuf(obj->channel, ENABLE, TMRB_WRITE_REG_SEPARATE);
|
|
|
|
TMRB_SetFlipFlop(obj->channel, &FFStruct);
|
|
|
|
// Start timer function
|
|
|
|
TMRB_SetRunState(obj->channel, TMRB_RUN);
|
|
|
|
}
|
|
|
|
|
|
|
|
void pwmout_pulsewidth(pwmout_t *obj, float seconds)
|
|
|
|
{
|
|
|
|
pwmout_pulsewidth_us(obj, (seconds * 1000000.0f));
|
|
|
|
}
|
|
|
|
|
|
|
|
void pwmout_pulsewidth_ms(pwmout_t *obj, int ms)
|
|
|
|
{
|
|
|
|
pwmout_pulsewidth_us(obj, (ms * 1000));
|
|
|
|
}
|
|
|
|
|
|
|
|
void pwmout_pulsewidth_us(pwmout_t *obj, int us)
|
|
|
|
{
|
|
|
|
float seconds = 0;
|
|
|
|
float value = 0;
|
|
|
|
|
|
|
|
seconds = (float)(us / 1000000.0f);
|
|
|
|
value = (((seconds / obj->period) * 100.0f) / 100.0f);
|
|
|
|
pwmout_write(obj, value);
|
|
|
|
}
|