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);
}
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.
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);
}
}
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);
}
}
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.
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);
}
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.
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.
If we are referring to the Maple core I’d suggest to move it elsewhere..
Too many cores these days…
// 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:
}
// 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:
}
I confirmed it, does not enable the timer device, only the channel.
I confirmed it, does not enable the timer device, only the channel.
or you were using the normal ones, then if you switch to the alternate ones for SPI1 then PWM works again?
or you were using the normal ones, then if you switch to the alternate ones for SPI1 then PWM works again?
Did you check if the end() method for the SPI port disables the SPI peripheral?
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);
}
}
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
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..
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.
( but perhaps the improvement didnt work)


