[Libmaple] pinMode() disables timer even if no PWM mode involved

rmdMoba
Thu May 11, 2017 11:09 am
Hello,
I’m using the STM32duino now for a while and enhanced my model-railroaders library to run on STM32.
This library extensivly uses timer 4 and compare interrupts. It does NOT use the related PWM output pins.
Sometimes my library stopped to work, and I found out, that this is always the case, when a pinMode() command ist running on one of the pins that can be used with PWM and timer 4.

The cause is this code snippet in wirish_digital_f1.cpp:
gpio_set_mode(PIN_MAP[pin].gpio_device, PIN_MAP[pin].gpio_bit, outputMode);

if (PIN_MAP[pin].timer_device != NULL) {
/* Enable/disable timer channels if we're switching into or
* out of PWM. */
timer_set_mode(PIN_MAP[pin].timer_device,
PIN_MAP[pin].timer_channel,
pwm ? TIMER_PWM : TIMER_DISABLED);
}


RogerClark
Thu May 11, 2017 10:59 pm
Thanks for reporting this.

If you have a github account, ( or could create one), the best way to submit your fix is via a Pull Request on github.

I will create an issue on github to track this asap.


edogaldo
Fri May 12, 2017 7:13 am
rmdMoba wrote:The timer should only be touched if the last mode was a pwm mode, or if the mode to be set is a PWM mode. In all other cases the timer should be left untouched.

I tried with this code, and it worked fine in my tests:
// remember old mode to see whether we have to change timer mode
gpio_pin_mode oldMode = gpio_get_mode(PIN_MAP[pin].gpio_device, PIN_MAP[pin].gpio_bit);

gpio_set_mode(PIN_MAP[pin].gpio_device, PIN_MAP[pin].gpio_bit, outputMode);

if (PIN_MAP[pin].timer_device != NULL) {
/* Enable/disable timer channels if we're switching into or
* out of PWM. */
if ( pwm || oldMode == GPIO_AF_OUTPUT_OD || oldMode == GPIO_AF_OUTPUT_PP ) {
timer_set_mode(PIN_MAP[pin].timer_device,
PIN_MAP[pin].timer_channel,
pwm ? TIMER_PWM : TIMER_DISABLED);
}
}


rmdMoba
Fri May 12, 2017 12:37 pm
Well, maybe a solution too. The gpio_set_mode() function disconnects the pin from the timer output if any standard outputmode is set ( no alternate function ). May be this is sufficient and the timer can be left untouched generally when setting any nonpwm mode.

victor_pv
Fri May 12, 2017 3:17 pm
edogaldo wrote:rmdMoba wrote:The timer should only be touched if the last mode was a pwm mode, or if the mode to be set is a PWM mode. In all other cases the timer should be left untouched.

I tried with this code, and it worked fine in my tests:
// remember old mode to see whether we have to change timer mode
gpio_pin_mode oldMode = gpio_get_mode(PIN_MAP[pin].gpio_device, PIN_MAP[pin].gpio_bit);

gpio_set_mode(PIN_MAP[pin].gpio_device, PIN_MAP[pin].gpio_bit, outputMode);

if (PIN_MAP[pin].timer_device != NULL) {
/* Enable/disable timer channels if we're switching into or
* out of PWM. */
if ( pwm || oldMode == GPIO_AF_OUTPUT_OD || oldMode == GPIO_AF_OUTPUT_PP ) {
timer_set_mode(PIN_MAP[pin].timer_device,
PIN_MAP[pin].timer_channel,
pwm ? TIMER_PWM : TIMER_DISABLED);
}
}


RogerClark
Fri May 12, 2017 9:34 pm
I agree.

It doesnt seem ncessary to disable the timer(s) at all.

The only downside I can think of, is increased power consumption, but would only effect anyone running from batteries, in whuch case they would probably write their own additional code to make sure any unused hardware is shut down.


rmdMoba
Sat May 13, 2017 8:49 pm
Hi all,
if we decide to never disable the timer in the pinMode function, the fix is even more simple:

gpio_set_mode(PIN_MAP[pin].gpio_device, PIN_MAP[pin].gpio_bit, outputMode);

if (pwm && PIN_MAP[pin].timer_device != NULL) {
/* Enable timer channels if we're switching into PWM. */
timer_set_mode(PIN_MAP[pin].timer_device,
PIN_MAP[pin].timer_channel,
TIMER_PWM);
}


RogerClark
Sat May 13, 2017 9:15 pm
Thanks

rmdMoba
Thu May 18, 2017 3:39 pm
Hello all,
I did several tests and my fix worked so far. But I found some strange behaviour with pins that provide two alternate functions, e.g. PWM and Serial or PWM and SPI. If once SPI or Serial was activated ( and then deactivated with the end method ) PWM did not work anymore. The other way round is OK: when starting PWM and later starting SPI or Serial this works, but switching back to PWM is not possible. This is the case with both variants – original and with my fix.
I could not find a reason for that so far. In the STM32F1-reference I didn’t find a way to exactly connect the pin to one or the other alternate function. In the GPIO Regs you only can generally connect the pin to alternate functions. And you can enable or disable these functions ( even both at the same time ). SPI or Serial seem to have priority over PWM, and if they once have been connected to the pin, there is no going back till reset. Does somene know more about that?

Maybe in practice this is not really a problem. I think it will be seldom that someone enables SPI on a pin and later will output a PWM Signal at the same pin.


victor_pv
Thu May 18, 2017 3:54 pm
rmdMoba wrote:Hello all,
I did several tests and my fix worked so far. But I found some strange behaviour with pins that provide two alternate functions, e.g. PWM and Serial or PWM and SPI. If once SPI or Serial was activated ( and then deactivated with the end method ) PWM did not work anymore. The other way round is OK: when starting PWM and later starting SPI or Serial this works, but switching back to PWM is not possible. This is the case with both variants – original and with my fix.
I could not find a reason for that so far. In the STM32F1-reference I didn’t find a way to exactly connect the pin to one or the other alternate function. In the GPIO Regs you only can generally connect the pin to alternate functions. And you can enable or disable these functions ( even both at the same time ). SPI or Serial seem to have priority over PWM, and if they once have been connected to the pin, there is no going back till reset. Does somene know more about that?

Maybe in practice this is not really a problem. I think it will be seldom that someone enables SPI on a pin and later will output a PWM Signal at the same pin.


edogaldo
Thu May 18, 2017 4:39 pm
Just an idea: reinvoking pinMode() doesn’t solve the issue?

rmdMoba
Thu May 18, 2017 8:34 pm
victor_pv wrote:I suspect the case may be that when SPI or Serial end, the pins are not configured completely back to default and something extra may need to be done.

RogerClark
Thu May 18, 2017 8:43 pm
can you post your test code

edogaldo
Thu May 18, 2017 8:59 pm
By the way, I just noticed this thread is in “STM core: Bugs and enhancements” section.
If we are referring to the Maple core I’d suggest to move it elsewhere..
Too many cores these days… :? :lol:

rmdMoba
Thu May 18, 2017 9:01 pm
I checked the output on PA7 with a scope:
// Test PWM on maplemini pin 4 / PA7 ( general Output / SPI and PWM )
#include <SPI.h>
const byte Led1P = 16; // Led active during PWM Output
const byte test1P = 4; // = port A7 ( SPI1-MOSI or TIM3 Ch2 )
SPIClass mySPI(1);
void setup() {
// put your setup code here, to run once:
pinMode( Led1P, OUTPUT );
digitalWrite( Led1P, HIGH );
delay(1000);

pinMode( test1P, INPUT_PULLUP );
delay( 1000 );
digitalWrite( Led1P, LOW );
pinMode( test1P, PWM ); // This PWM Sequence works fine
for ( byte i=0; i<220; i++ ) {
//analogWrite( test1P, i );
pwmWrite( test1P, i*257 );
delay( 20 );
}
digitalWrite( Led1P, HIGH );
delay( 1000 );

pinMode( test1P, OUTPUT ); // General IO - works fine too
for ( byte i=0; i<11; i++ ) {
digitalWrite(test1P, i&1 );
delay( 5 );
}
pinMode( test1P, INPUT );
delay(1000);
mySPI.begin(); // SPI output - works fine
mySPI.beginTransaction(SPISettings(550000, MSBFIRST, SPI_MODE0, DATA_SIZE_16BIT));
for ( byte i=0; i<11; i++ ) {
mySPI.write(0x5555);
delay( 5 );
}
mySPI.endTransaction();
mySPI.end();
delay(1000);
pinMode( test1P, OUTPUT ); // general OUTPUT - it works
digitalWrite( test1P, LOW );
delay(500);
digitalWrite( test1P, HIGH );
delay(1000);
digitalWrite( Led1P, LOW );
pinMode( test1P, PWM ); // The following PWM sequence doesn't work
for ( int i=220; i>0; i-- ) { // pin test1P seems to be in high impedance
//analogWrite( test1P, i ); // state
pwmWrite( test1P, i*257 );
delay( 20 );
}
digitalWrite( Led1P, HIGH );
delay(1000);
mySPI.begin(); // This again works
mySPI.beginTransaction(SPISettings(550000, MSBFIRST, SPI_MODE0, DATA_SIZE_16BIT));
for ( int i=0; i<11; i++ ) {
mySPI.write(0x5555);
delay( 5 );
}
mySPI.endTransaction();
mySPI.end();

/* Serial1.begin( 1200 );
delay( 1000);
Serial1.println("start-dimming");
delay(1000); */

}
void loop() {
// put your main code here, to run repeatedly:

}


victor_pv
Thu May 18, 2017 9:27 pm
rmdMoba wrote:I checked the output on PA7 with a scope:
// Test PWM on maplemini pin 4 / PA7 ( general Output / SPI and PWM )
#include <SPI.h>
const byte Led1P = 16; // Led active during PWM Output
const byte test1P = 4; // = port A7 ( SPI1-MOSI or TIM3 Ch2 )
SPIClass mySPI(1);
void setup() {
// put your setup code here, to run once:
pinMode( Led1P, OUTPUT );
digitalWrite( Led1P, HIGH );
delay(1000);

pinMode( test1P, INPUT_PULLUP );
delay( 1000 );
digitalWrite( Led1P, LOW );
pinMode( test1P, PWM ); // This PWM Sequence works fine
for ( byte i=0; i<220; i++ ) {
//analogWrite( test1P, i );
pwmWrite( test1P, i*257 );
delay( 20 );
}
digitalWrite( Led1P, HIGH );
delay( 1000 );

pinMode( test1P, OUTPUT ); // General IO - works fine too
for ( byte i=0; i<11; i++ ) {
digitalWrite(test1P, i&1 );
delay( 5 );
}
pinMode( test1P, INPUT );
delay(1000);
mySPI.begin(); // SPI output - works fine
mySPI.beginTransaction(SPISettings(550000, MSBFIRST, SPI_MODE0, DATA_SIZE_16BIT));
for ( byte i=0; i<11; i++ ) {
mySPI.write(0x5555);
delay( 5 );
}
mySPI.endTransaction();
mySPI.end();
delay(1000);
pinMode( test1P, OUTPUT ); // general OUTPUT - it works
digitalWrite( test1P, LOW );
delay(500);
digitalWrite( test1P, HIGH );
delay(1000);
digitalWrite( Led1P, LOW );
pinMode( test1P, PWM ); // The following PWM sequence doesn't work
for ( int i=220; i>0; i-- ) { // pin test1P seems to be in high impedance
//analogWrite( test1P, i ); // state
pwmWrite( test1P, i*257 );
delay( 20 );
}
digitalWrite( Led1P, HIGH );
delay(1000);
mySPI.begin(); // This again works
mySPI.beginTransaction(SPISettings(550000, MSBFIRST, SPI_MODE0, DATA_SIZE_16BIT));
for ( int i=0; i<11; i++ ) {
mySPI.write(0x5555);
delay( 5 );
}
mySPI.endTransaction();
mySPI.end();

/* Serial1.begin( 1200 );
delay( 1000);
Serial1.println("start-dimming");
delay(1000); */

}
void loop() {
// put your main code here, to run repeatedly:

}


rmdMoba
Fri May 19, 2017 7:14 am
victor_pv wrote:So you will need to enable the timer device again.

victor_pv
Fri May 19, 2017 4:04 pm
rmdMoba wrote:victor_pv wrote:So you will need to enable the timer device again.

rmdMoba
Fri May 19, 2017 9:21 pm
victor_pv wrote:
I confirmed it, does not enable the timer device, only the channel.

victor_pv
Fri May 19, 2017 9:32 pm
rmdMoba wrote:victor_pv wrote:
I confirmed it, does not enable the timer device, only the channel.

rmdMoba
Fri May 19, 2017 9:36 pm
victor_pv wrote:
or you were using the normal ones, then if you switch to the alternate ones for SPI1 then PWM works again?

victor_pv
Fri May 19, 2017 9:38 pm
rmdMoba wrote:victor_pv wrote:
or you were using the normal ones, then if you switch to the alternate ones for SPI1 then PWM works again?

rmdMoba
Fri May 19, 2017 9:42 pm
victor_pv wrote:
Did you check if the end() method for the SPI port disables the SPI peripheral?

rmdMoba
Sat May 20, 2017 2:39 pm
After several tests I did not find a reason why pwm doesn’t work again after SPI has been active. The only solution I found is to remap the SPI pins ( or execute a hardware reset ). It seems that SPI blocks the AF on this pin if it once has been activated. Even setting all SPI registers back to ‘0’ (the state after reset ) does not help. The same seems to be true on pins that share UART and PWM as AF.

Because PWM works after remapping SPI this is obviously not a problem of PWM/timer channel configuration.

The changes to pinMode() now don’t stop attached IRQ’s when setting non PWM modes. The latest version of the fix is like this:
if (PIN_MAP[pin].timer_device != NULL) {
if ( pwm ) { // we're switching into PWM, enable timer channels
timer_set_mode(PIN_MAP[pin].timer_device,
PIN_MAP[pin].timer_channel,
TIMER_PWM );
} else { // disable channel output in non pwm-Mode
timer_cc_disable(PIN_MAP[pin].timer_device,
PIN_MAP[pin].timer_channel);
}
}


RogerClark
Fri May 26, 2017 7:04 am
Can we change this one step at a time.

I think we’ve established that pinMode does not need to disable the whole timer

Can a PR be generated for just the pinMode change

The SPI issue looks more complicated and can be applied separately to the pinMode change


edogaldo
Fri May 26, 2017 7:27 am
RogerClark wrote:Can a PR be generated for just the pinMode change

stevestrong
Fri May 26, 2017 7:33 am
edogaldo wrote:
Hi Roger, there is PR #288 which addresses it but I don’t understand why the guy added a second commit which, on my opinion, breaks things again..

edogaldo
Fri May 26, 2017 7:41 am
stevestrong wrote:edogaldo, can you please detail which things exactly get broken with this PR?

RogerClark
Fri May 26, 2017 7:42 am
It looks OK kinda OK to me

https://github.com/rogerclarkmelbourne/ … /288/files


rmdMoba
Fri May 26, 2017 12:26 pm
edogaldo wrote:A question as I’m not that expert with timers: can’t we have any valid combination (also in a possible future variant) in which 2 pins can map to the same couple {timer, channel}?

RogerClark
Mon Jul 31, 2017 4:05 am
Do we have a decision on this ?

victor_pv
Mon Jul 31, 2017 1:00 pm
edogaldo wrote: Fri May 26, 2017 7:41 am

stevestrong
Mon Jul 31, 2017 1:08 pm
I don’t know any application which would get broken by this commit.
If in the future some problem arise, then we could still analyze the situation and, if necessary, revert it.

So I am in favor of this commit.


RogerClark
Mon Jul 31, 2017 9:27 pm
I will need to check if the PR got updated, as I thought someone posted an improvement to the PR.

( but perhaps the improvement didnt work)


victor_pv
Mon Aug 21, 2017 5:03 pm
This thread seems to be focused on the libmaple based core, so I am moving it to that forums.

RogerClark
Mon Aug 21, 2017 9:10 pm
We probably need a LibMaple Core section. I will log in as super admin and create it

Leave a Reply

Your email address will not be published. Required fields are marked *