Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Fork of mbed-dev by
targets/hal/TARGET_NXP/TARGET_LPC81X/pwmout_api.c
- Committer:
- <>
- Date:
- 2016-09-02
- Revision:
- 144:ef7eb2e8f9f7
- Parent:
- 137:28723fa54344
File content as of revision 144:ef7eb2e8f9f7:
/* 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 "mbed_assert.h"
#include "pwmout_api.h"
#include "cmsis.h"
#include "pinmap.h"
#include "mbed_error.h"
// Ported from LPC824 and adapted.
#if DEVICE_PWMOUT
#define PWM_IRQn SCT_IRQn
// Bit flags for used SCT Outputs
static unsigned char sct_used = 0;
static int sct_inited = 0;
// Find available output channel
// Max number of PWM outputs is 4 on LPC812
static int get_available_sct() {
int i;
// Find available output channel 0..3
// Also need one Match register per channel
for (i = 0; i < CONFIG_SCT_nOU; i++) {
if ((sct_used & (1 << i)) == 0)
return i;
}
return -1;
}
// Any Port pin may be used for PWM.
// Max number of PWM outputs is 4
void pwmout_init(pwmout_t* obj, PinName pin) {
MBED_ASSERT(pin != (PinName)NC);
int sct_n = get_available_sct();
if (sct_n == -1) {
error("No available SCT Output");
}
sct_used |= (1 << sct_n);
obj->pwm = (LPC_SCT_TypeDef*)LPC_SCT;
obj->pwm_ch = sct_n;
LPC_SCT_TypeDef* pwm = obj->pwm;
// Init SCT on first use
if (! sct_inited) {
sct_inited = 1;
// Enable the SCT clock
LPC_SYSCON->SYSAHBCLKCTRL |= (1 << 8);
// Clear peripheral reset the SCT:
LPC_SYSCON->PRESETCTRL |= (1 << 8);
// Two 16-bit counters, autolimit (ie reset on Match_0)
pwm->CONFIG |= ((0x3 << 17) | 0x01);
// halt and clear the counter
pwm->CTRL_U |= (1 << 2) | (1 << 3);
// System Clock (30 Mhz) -> Prescaler -> us_ticker (1 MHz)
pwm->CTRL_U &= ~(0x7F << 5);
pwm->CTRL_U |= (((SystemCoreClock/1000000 - 1) & 0x7F) << 5);
pwm->EVENT[0].CTRL = (1 << 12) | 0; // Event_0 on Match_0
pwm->EVENT[0].STATE = 0xFFFFFFFF; // All states
// unhalt the counter:
// - clearing bit 2 of the CTRL register
pwm->CTRL_U &= ~(1 << 2);
}
// LPC81x has only one SCT and 4 Outputs
// LPC82x has only one SCT and 6 Outputs
// LPC1549 has 4 SCTs and 16 Outputs
switch(sct_n) {
case 0:
// SCTx_OUT0
LPC_SWM->PINASSIGN[6] &= ~0xFF000000;
LPC_SWM->PINASSIGN[6] |= (pin << 24);
break;
case 1:
// SCTx_OUT1
LPC_SWM->PINASSIGN[7] &= ~0x000000FF;
LPC_SWM->PINASSIGN[7] |= (pin);
break;
case 2:
// SCTx_OUT2
LPC_SWM->PINASSIGN[7] &= ~0x0000FF00;
LPC_SWM->PINASSIGN[7] |= (pin << 8);
break;
case 3:
// SCTx_OUT3
LPC_SWM->PINASSIGN[7] &= ~0x00FF0000;
LPC_SWM->PINASSIGN[7] |= (pin << 16);
break;
default:
break;
}
pwm->EVENT[sct_n + 1].CTRL = (1 << 12) | (sct_n + 1); // Event_n on Match_n
pwm->EVENT[sct_n + 1].STATE = 0xFFFFFFFF; // All states
pwm->OUT[sct_n].SET = (1 << 0); // All PWM channels are SET on Event_0
pwm->OUT[sct_n].CLR = (1 << (sct_n + 1)); // PWM ch is CLRed on Event_(ch+1)
// default to 20ms: standard for servos, and fine for e.g. brightness control
pwmout_period_ms(obj, 20); // 20ms period
pwmout_write (obj, 0.0); // 0ms pulsewidth, dutycycle 0
}
void pwmout_free(pwmout_t* obj) {
// PWM channel is now free
sct_used &= ~(1 << obj->pwm_ch);
// Disable the SCT clock when all channels free
if (sct_used == 0) {
LPC_SYSCON->SYSAHBCLKCTRL &= ~(1 << 8);
sct_inited = 0;
};
}
// Set new dutycycle (0.0 .. 1.0)
void pwmout_write(pwmout_t* obj, float value) {
//value is new dutycycle
if (value < 0.0f) {
value = 0.0;
} else if (value > 1.0f) {
value = 1.0;
}
// Match_0 is PWM period. Compute new endtime of pulse for current channel
uint32_t t_off = (uint32_t)((float)(obj->pwm->MATCHREL[0].U) * value);
obj->pwm->MATCHREL[(obj->pwm_ch) + 1].U = t_off; // New endtime
// Clear OxRES (conflict resolution register) bit first, effect of simultaneous set and clear on output x
int offset = (obj->pwm_ch * 2);
obj->pwm->RES &= ~(0x3 << offset);
if (value == 0.0f) { // duty is 0%
// Clear output
obj->pwm->RES |= (0x2 << offset);
// Set CLR event to be same as SET event, makes output to be 0 (low)
obj->pwm->OUT[(obj->pwm_ch)].CLR = (1 << 0);
} else {
// Set output
obj->pwm->RES |= (0x1 << offset);
// Use normal CLR event (current SCT ch + 1)
obj->pwm->OUT[(obj->pwm_ch)].CLR = (1 << ((obj->pwm_ch) + 1));
}
}
// Get dutycycle (0.0 .. 1.0)
float pwmout_read(pwmout_t* obj) {
uint32_t t_period = obj->pwm->MATCHREL[0].U;
//Sanity check
if (t_period == 0) {
return 0.0;
};
uint32_t t_off = obj->pwm->MATCHREL[(obj->pwm_ch) + 1].U;
float v = (float)t_off/(float)t_period;
//Sanity check
return (v > 1.0f) ? (1.0f) : (v);
}
// Set the PWM period, keeping the duty cycle the same (for this channel only!).
void pwmout_period(pwmout_t* obj, float seconds){
pwmout_period_us(obj, seconds * 1000000.0f);
}
// Set the PWM period, keeping the duty cycle the same (for this channel only!).
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 (for this channel only!).
void pwmout_period_us(pwmout_t* obj, int us) {
uint32_t t_period = obj->pwm->MATCHREL[0].U; // Current PWM period
obj->pwm->MATCHREL[0].U = (uint32_t)us; // New PWM period
// Sanity check
if (t_period == 0) {
return;
}
else {
int cnt = sct_used;
int ch = 0;
// Update match period for exising PWM channels
do {
// Get current pulse width
uint32_t t_off = obj->pwm->MATCHREL[ch + 1].U;
// Get the duty
float v = (float)t_off/(float)t_period;
// Update pulse width for this channel
obj->pwm->MATCHREL[ch + 1].U = (uint32_t)((float)us * (float)v);
// Get next used SCT channel
cnt = cnt >> 1;
ch++;
} while (cnt != 0);
}
}
//Set pulsewidth
void pwmout_pulsewidth(pwmout_t* obj, float seconds) {
pwmout_pulsewidth_us(obj, seconds * 1000000.0f);
}
//Set pulsewidth
void pwmout_pulsewidth_ms(pwmout_t* obj, int ms){
pwmout_pulsewidth_us(obj, ms * 1000);
}
//Set pulsewidth
void pwmout_pulsewidth_us(pwmout_t* obj, int us) {
if (us == 0) { // pulse width is 0
// Set CLR event to be same as SET event, makes output to be 0 (low)
obj->pwm->OUT[(obj->pwm_ch)].CLR = (1 << 0);
} else {
// Use normal CLR event (current SCT ch + 1)
obj->pwm->OUT[(obj->pwm_ch)].CLR = (1 << ((obj->pwm_ch) + 1));
}
//Should add Sanity check to make sure pulsewidth < period!
obj->pwm->MATCHREL[(obj->pwm_ch) + 1].U = (uint32_t)us; // New endtime for this channel
}
#endif
