Skip to content

Create pwmout_api.c #906

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Feb 24, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions libraries/mbed/targets/hal/TARGET_NXP/TARGET_LPC81X/device.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
#define DEVICE_SERIAL_FC 1

#define DEVICE_I2C 1
#define DEVICE_I2CSLAVE 0
#define DEVICE_I2CSLAVE 1
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this the target which will need modifications to have slave enabled?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Martin,

As explained in the comments for the pull request, the I2C slave can be
enabled and the slave block read/write operations are fine. The additional
work in I2CSlave.cpp is needed for a correct operation of the slave byte
read/write operations. This change includes platforms such as the LPC812 and
LPC824 because they have different I2C engines for master and slave.

Again, the currently accepted LPC824 I2C slave lib has that same problem and
doesn't work correctly for the slave byte read/writes.

Wim


From: Martin Kojtal [mailto:[email protected]]
Sent: donderdag 12 februari 2015 9:32
To: mbedmicro/mbed
Cc: Wim
Subject: Re: [mbed] Create pwmout_api.c (#906)

In libraries/mbed/targets/hal/TARGET_NXP/TARGET_LPC81X/device.h
#906 (comment) :

@@ -29,7 +29,7 @@
#define DEVICE_SERIAL_FC 1

#define DEVICE_I2C 1
-#define DEVICE_I2CSLAVE 0
+#define DEVICE_I2CSLAVE 1

Isn't this the target which will need modifications to have slave enabled?

Reply to this email directly or view it on
https://github.com/mbedmicro/mbed/pull/906/files#r24565131 GitHub.
<https://github.com/notifications/beacon/AKMUNJ5mrGex1x6ADfFng6gpyjnPdGHRks5
nrFyBgaJpZM4DfF7f.gif>


#define DEVICE_SPI 1
#define DEVICE_SPISLAVE 1
Expand All @@ -40,7 +40,7 @@

#define DEVICE_ETHERNET 0

#define DEVICE_PWMOUT 0
#define DEVICE_PWMOUT 1

#define DEVICE_SEMIHOST 0
#define DEVICE_LOCALFILESYSTEM 0
Expand Down
5 changes: 5 additions & 0 deletions libraries/mbed/targets/hal/TARGET_NXP/TARGET_LPC81X/objects.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ struct spi_s {
unsigned char spi_n;
};

struct pwmout_s {
LPC_SCT_TypeDef* pwm;
uint32_t pwm_ch;
};

#include "gpio_object.h"

#ifdef __cplusplus
Expand Down
226 changes: 226 additions & 0 deletions libraries/mbed/targets/hal/TARGET_NXP/TARGET_LPC81X/pwmout_api.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
/* 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++) {
// for (i = 0; i < 4; 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 != (uint32_t)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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can be this retrieved from hw, as a run time check, no variable involved?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Martin,

it doesnt look like it is possible to reliably retrieve free outputs/SCT
counters by reading the hardware. The SCT does not support an ''idle''
register as is available for the MRT.

I considered checking the timer reload value and assuming the timer is free
when the value is zero. However, user code may want to set the value to zero
to temporarily disable the PWM. So this also needs admin overhead for
reliable detection. The current solution is similar to what I have seen for
peripherals such as I2C on other mbed platforms. It is also the same
solution that is already used for the current LPC824 mbed PWM library.

Wim

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 &= ~(0x1);
pwm->CONFIG |= (1 << 17);

// halt and clear the counter
pwm->CTRL_L |= (1 << 2) | (1 << 3);

// System Clock (30 Mhz) -> Prescaler -> us_ticker (1 MHz)
pwm->CTRL_L &= ~(0x7F << 5);
pwm->CTRL_L |= (((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_L &= ~(1 << 2);

// Not using IRQs
//NVIC_SetVector(PWM_IRQn, (uint32_t)pwm_irq_handler);
//NVIC_EnableIRQ(PWM_IRQn);
}

// 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].L) * value);
obj->pwm->MATCHREL[(obj->pwm_ch) + 1].L = t_off; // New endtime
}

// Get dutycycle (0.0 .. 1.0)
float pwmout_read(pwmout_t* obj) {
uint32_t t_period = obj->pwm->MATCHREL[0].L;

//Sanity check
if (t_period == 0) {
return 0.0;
};

uint32_t t_off = obj->pwm->MATCHREL[(obj->pwm_ch) + 1].L;
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].L; // Current PWM period
obj->pwm->MATCHREL[0].L = (uint64_t)us; // New PWM period

//Keep the dutycycle for the new PWM period
//Should really do this for all active channels!!
//This problem exists in all mbed libs.

//Sanity check
if (t_period == 0) {
return;
// obj->pwm->MATCHREL[(obj->pwm_ch) + 1].L = 0; // New endtime for this channel
}
else {
uint32_t t_off = obj->pwm->MATCHREL[(obj->pwm_ch) + 1].L;
float v = (float)t_off/(float)t_period;
obj->pwm->MATCHREL[(obj->pwm_ch) + 1].L = (uint64_t)((float)us * (float)v); // New endtime for this channel
}
}


//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) {

//Should add Sanity check to make sure pulsewidth < period!
obj->pwm->MATCHREL[(obj->pwm_ch) + 1].L = (uint64_t)us; // New endtime for this channel
}

#endif