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/TARGET_NXP/TARGET_LPC81X/pwmout_api.c
- Committer:
- <>
- Date:
- 2016-10-28
- Revision:
- 149:156823d33999
- Parent:
- targets/hal/TARGET_NXP/TARGET_LPC81X/pwmout_api.c@ 144:ef7eb2e8f9f7
File content as of revision 149:156823d33999:
/* 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