Author Topic: bad behavior on an attempt to implement multiplexing on STM32  (Read 3260 times)

0 Members and 1 Guest are viewing this topic.

Offline RawCodeTopic starter

  • Regular Contributor
  • *
  • Posts: 73
Hello there!
I'm quite new to the STM32 mcu, so I'm trying to implement basic things to understand how it works. To do so, I tried to multiplex a 4 rows by 11 columns LED array, using two timers, one to drive the 4 rows with the PWM output and another timer to switch the LEDs in the columns.

My code kinda works, but it works not as I expected: for example, it turns on in the same row two LEDs, one of them is switched on as it should be, the other led should be lit in another row and not in this row. Same behavior in the other rows. It seems like the PWM switching is too slow in comparison to the switching of the columns, and because of this there is this this "ghosting" effect, but i set the timers with the same prescaler and the same period count in both timers, so i don't know (prescaler set to 32000 and period count set to 1 @ timer clock 32MHz)

This is my code:
Code: [Select]
void displayNumber(struct time input)
{
volatile uint16_t intTimerOld=0;
uint8_t cnt=0;
htim2.Instance->CCR1 = 100;// set the duty cycle to 100
htim2.Instance->CCR2 = 100;
htim2.Instance->CCR3 = 100;
htim2.Instance->CCR4 = 100;

HAL_TIM_Base_Start_IT(&htim3);

while(intTimer < 6500)
{
if(intTimerOld != intTimer)
{
switch(cnt)
{
case 0:
HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_1);//stop all the PWM
HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_2);
HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_3);
HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_4);
resetPin();
associateNumber(0);
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);//start the first PWM
cnt++;
break;

case 1:
HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_1);
HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_2);
HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_3);
HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_4);
resetPin();
associateNumber(0);
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2);
cnt++;
break;
case 2:
HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_1);
HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_2);
HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_3);
HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_4);
resetPin();
associateNumber(0);
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_3);
cnt++;
break;
case 3:
HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_1);
HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_2);
HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_3);
HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_4);
resetPin();
associateNumber(0);
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_4);
cnt++;
break;
case 4:
HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_1);
HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_2);
HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_3);
HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_4);
resetPin();
associateNumber(10);
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2);
cnt=0;
break;
}
intTimerOld = intTimer;
}
}
resetPin();
htim2.Instance->CCR1 = 0;
htim2.Instance->CCR2 = 0;
htim2.Instance->CCR3 = 0;
htim2.Instance->CCR4 = 0;
HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_1);
HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_2);
HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_3);
HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_4);
HAL_TIM_Base_Stop_IT(&htim3);
intTimerOld=0;
intTimer=0;
}

The function resetPin() is a custom function that I made, which reset to low all the pin of the columns, using for example "HAL_GPIO_WritePin(GPIOC, MUX_9_Pin, GPIO_PIN_RESET)"
The function associatePin() is also a custom function that I made, which select the right column by its argument and it switch it to high, using for example "HAL_GPIO_WritePin(GPIOA, MUX_1_Pin, GPIO_PIN_SET)"

Do you have any suggestions to achieve a proper multiplexing? What am I doing wrong? Can you help me?
« Last Edit: July 08, 2019, 09:53:52 am by RawCode »
 

Offline capt bullshot

  • Super Contributor
  • ***
  • Posts: 3033
  • Country: de
    • Mostly useless stuff, but nice to have: wunderkis.de
Re: bad behavior on an attempt to implement multiplexing on STM32
« Reply #1 on: July 08, 2019, 10:41:47 am »
Your source isn't complete, so I can't see the whole point of your code, and which way you're using the timers.

Anyway, the case 4: of switch(cnt) looks suspicious to me since it kind of repeats case 1:

In general, I looks OK to use four timer outputs in either PWM or one-shot mode to generate a multiplexing pattern for variable intensity of the LEDs. Another timer would drive an interrupt routine to advance the common columns. Within this interrupt routine one would (in this order)

turn off all rows
turn off active column
turn on next column
initiate PWM or one-shot timers with the new column data
return and wait for next interrupt

Some care has to be taken to start the row timers synchronously enough to have a non-flickering result. I'd program the row timers period to the column interrupt rate, and use the row timers in one-shot mode.
Safety devices hinder evolution
 
The following users thanked this post: RawCode

Offline andersm

  • Super Contributor
  • ***
  • Posts: 1198
  • Country: fi
Re: bad behavior on an attempt to implement multiplexing on STM32
« Reply #2 on: July 08, 2019, 11:59:41 am »
Note that since the intTimerOld variable is used only inside that one function, there's no point in declaring it volatile.
 
The following users thanked this post: RawCode

Offline RawCodeTopic starter

  • Regular Contributor
  • *
  • Posts: 73
Re: bad behavior on an attempt to implement multiplexing on STM32
« Reply #3 on: July 08, 2019, 12:22:55 pm »
thanks for your reply.

Anyway, the case 4: of switch(cnt) looks suspicious to me since it kind of repeats case 1:

yes, i know. I'm doing it on purpose: this project should be something like a watch, and to show a dot between the hours and the minutes i'm adding the 4th case to turn on the 11th column in the second row as a dot.

In general, I looks OK to use four timer outputs in either PWM or one-shot mode to generate a multiplexing pattern for variable intensity of the LEDs. Another timer would drive an interrupt routine to advance the common columns. Within this interrupt routine one would (in this order)

turn off all rows
turn off active column
turn on next column
initiate PWM or one-shot timers with the new column data
return and wait for next interrupt

I'm doing something like you said: i'm using a timer to output the PWM signals and another timer to trigger an interrupt every 1ms to change row(if my calculations are correct). In the interrupt routine btw i'm just updating a volatile variable that counts how many times the timer triggers the interrupt. I'm using this number also as the end of the multiplexing routine. So, the whole operation is done in the main.c, and in the interrupt routine i'm just updating a variable. am i doing it wrong?

Some care has to be taken to start the row timers synchronously enough to have a non-flickering result. I'd program the row timers period to the column interrupt rate, and use the row timers in one-shot mode.

btw i didn't quite understand this. what do you mean by program the row timers period to the column interrupt rate? and what is the one-shot mode? what is its purpose?

anyway. this is my whole code(is quite long, i'll post it on PasteBin). There are 3 timers setted, one of them (TIM9) is used to drive a small motor and it works fine. TIM 3 is the timer uset to switch the row, and TIM2 is used for the PWM signals.
https://pastebin.com/r9vKT5wT //main code
https://pastebin.com/PzP6jGgy //interrupt service routines

As you can notice in the picture the 4th row(the last in the right) has its 11th column lit as the second row, and it should not be lit. Btw, this ghosting behavior is casual.
« Last Edit: July 08, 2019, 12:59:13 pm by RawCode »
 

Offline capt bullshot

  • Super Contributor
  • ***
  • Posts: 3033
  • Country: de
    • Mostly useless stuff, but nice to have: wunderkis.de
Re: bad behavior on an attempt to implement multiplexing on STM32
« Reply #4 on: July 08, 2019, 01:14:41 pm »
So I've skimmed over your code:
It's not the way I'd do that task, but never mind. I'd put the multiplexing within the TIM3 interrupt, so then main task doesn't have to bother with that.

Didn't follow your logic for the intentional "case 4:", but my common sense tells me that this can't be the right way, you'd light up the decimal point in "case 1:" additionally to the number.

I can see one issue with the timers: Setting the prescaler to 32000 and the period to 1 makes the counter effectively count from zero to one and reset then to zero. No way to generate a useful PWM signal that way with TIM2. What you want is to set the prescaler to a value that allows the counter to count up to a as high as possible value without overflowing. So assuming a 32MHz clock, one could set the prescaler to 1 and the period to 32000 (minus one -> 31999) to achieve 1ms period. Now you can set the CCR registers to any value from 0 to 31999 to set the LED intensity.

You can apply the same to TIM3 (generating the interrupt) but your way should work too here. Otherwise, you could use TIM2 reload event to trigger the interrupt, as it seems to have the same period and don't need TIM3 anymore.

One-shot mode (can't be be bothered to look up the term STM uses for their timers, could be named differentyl) means you start the timer and it runs to the terminal value (the set period), then stops. For the next cycle you'd have to start it again - as far as I can see you're doing that anyway. One-shot mode would just turn off your LEDs after one PWM cycle, so you'd just have to load the CCR registers with their new values and start the timer again. For this to work, program the timer outputs to start the cycle with "LED on" and "LED off" at compare event.

Regarding the "ghosting" between the second and fourth line of LEDs: This isn't the typical "ghosting" one can usually see with multiplexed LEDs. Ghosting results when one common line is turned off and the next is turned on while the individual lines aren't fully switched over to their next state. This would rather look like one LED above or below a lit LED in the vertical lines would slightly glow.

Looks like you're using transistors for the row and column drive, their turn-off delay often causes ghosting with multiplexed LEDs, but from what I can tell this isn't a issue (yet) here.
Safety devices hinder evolution
 
The following users thanked this post: RawCode

Offline capt bullshot

  • Super Contributor
  • ***
  • Posts: 3033
  • Country: de
    • Mostly useless stuff, but nice to have: wunderkis.de
Re: bad behavior on an attempt to implement multiplexing on STM32
« Reply #5 on: July 08, 2019, 01:26:18 pm »
Just an addition:

I thing I've got your row /  column scheme the wrong way when I wrote about the ghosting.
Your photo indeed shows some kind of ghosting, I guess it's an result of having the prescaler set to 32000 and the period set to 1 - so depending on the timing of your main() in relation to the timer overflowing, simply the wrong column of LEDs get powered. Due to the prescaler set to 32000 you've got very coarse control over the TIM2 outputs, so I guess they simply sometimes skip a columg and sometime don't.
Safety devices hinder evolution
 
The following users thanked this post: RawCode

Offline RawCodeTopic starter

  • Regular Contributor
  • *
  • Posts: 73
Re: bad behavior on an attempt to implement multiplexing on STM32
« Reply #6 on: July 08, 2019, 02:29:11 pm »
Thanks for the explanation.

I modified my code as you suggested, changing the prescaler and the period to 1 and 32000 respectively in TIM2 (i also did it in TIM3), but there is still this ghosting problem.
yes, i'm using some transistor to drive the LED, but i don't think that they're causing this issue: the frequencies involved are to low, aren't they?

i also tried to don't use the associatePin() and resetPin(), but the ghosting is still there.
Any other suggestions?

Anyway i did another photo that shows better this ghosting problem. It seems like the process involved in switching on/switching off the PWM outputs is too slow
« Last Edit: July 08, 2019, 02:30:57 pm by RawCode »
 

Offline capt bullshot

  • Super Contributor
  • ***
  • Posts: 3033
  • Country: de
    • Mostly useless stuff, but nice to have: wunderkis.de
Re: bad behavior on an attempt to implement multiplexing on STM32
« Reply #7 on: July 08, 2019, 03:02:30 pm »
Assuming the ghosting to be seen between the middle two columns, I can think of two reasons for that:

- Slow transistor turn-off (yes, this can still be visible at 1ms scanning clock) - try to add more dead time between turning off the LEDs and turning on the next column.

- Update behaviour of the timer registers - I didn't look into these details of your code, and don't know what the HAL calls you've used do in detail, I do prefer to write the registers directly (use HAL only for first time initialization). Using HAL doesn't replace reading the manual to get a good understanding of how the peripherals behave. So what might be the issue: Updating the the CCR registers doesn't change the timer output at the same instant, but is delayed depending on the particular configuration, this also might cause the ghosting.

If this is the first time you use these timers, you might want to draw some timing diagrams using pen and paper showing the timer operation in relation to the register updates you do, you'll have to dig through the timer section of the reference manual to get there. From my experience, this helps to get an idea of when the HAL functions and CCR register writes actually update the output pin, then you'll get an idea of how to program the timer to act like you want it to. Maybe it's already doing so, then look at the transistors again ...

Another (simple) way to rule out the transistors would just be to completely skip TIM2 and use GPIO for both columns and rows. Once this works, switch your code to use the timer again.
Safety devices hinder evolution
 
The following users thanked this post: RawCode

Offline RawCodeTopic starter

  • Regular Contributor
  • *
  • Posts: 73
Re: bad behavior on an attempt to implement multiplexing on STM32
« Reply #8 on: July 08, 2019, 10:15:07 pm »
Thanks for your suggestions.
I managed to solve the ghosting problem: slowing down the timer to a human period, i noticed some glitches during the switching-on/switching-off cycles, even configuring the register instead of using the HAL library. So i tried to remove those glitches introducing some "blanking" case in the switch, between every switch-on.
And there we are, a multiplexing without any ghosting! I have to tune a bit the TIM3 and TIM2, to get a non flashing behavior and a the LEDs not too much dimmed, but it works :)

Thank you all sinceriously!

This is my code now:
Code: [Select]
void displayNumber(struct time input)
{
uint16_t intTimerOld=0;
volatile uint8_t cnt=0;
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2);
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_3);
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_4);
htim2.Instance->CCR1 = 0;
htim2.Instance->CCR2 = 0;
htim2.Instance->CCR3 = 0;
htim2.Instance->CCR4 = 0;

HAL_TIM_Base_Start_IT(&htim3);

while(intTimer < 1000)
{
if(intTimerOld != intTimer)
{
switch(cnt)
{
case 0:
resetPin();
htim2.Instance->CCR4 = 0;
cnt++;
break;

case 1:
associateNumber(input.minutesD);
cnt++;
break;

case 2:
htim2.Instance->CCR1 = 12499;
cnt++;
break;

case 3:
resetPin();
htim2.Instance->CCR1 = 0;
cnt++;
break;

case 4:
associateNumber(input.minutesU);
//associateNumber(10);
cnt++;
break;

case 5:
htim2.Instance->CCR2 = 12499;
cnt++;
break;

case 6:
resetPin();
htim2.Instance->CCR2 = 0;
cnt++;
break;

case 7:
associateNumber(10);
cnt++;
break;

case 8:
htim2.Instance->CCR2 = 12499;
cnt++;
break;

case 9:
resetPin();
htim2.Instance->CCR2 = 0;
cnt++;
break;

case 10:
associateNumber(input.secondsD);
cnt++;
break;

case 11:
htim2.Instance->CCR3 = 12499;
cnt++;
break;

case 12:
resetPin();
htim2.Instance->CCR3 = 0;
cnt++;
break;

case 13:
associateNumber(input.secondsU);
cnt++;
break;

case 14:
htim2.Instance->CCR4 = 12499;
cnt=0;
break;

}
intTimerOld = intTimer;
}
}
resetPin();
htim2.Instance->CCR1 = 0;
htim2.Instance->CCR2 = 0;
htim2.Instance->CCR3 = 0;
htim2.Instance->CCR4 = 0;
HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_1);
HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_2);
HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_3);
HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_4);
HAL_TIM_Base_Stop_IT(&htim3);
intTimerOld=0;
intTimer=0;
}
 
The following users thanked this post: capt bullshot

Offline capt bullshot

  • Super Contributor
  • ***
  • Posts: 3033
  • Country: de
    • Mostly useless stuff, but nice to have: wunderkis.de
Re: bad behavior on an attempt to implement multiplexing on STM32
« Reply #9 on: July 09, 2019, 05:31:03 am »
You're welcome.

Quote
I managed to solve the ghosting problem: slowing down the timer to a human period, i noticed some glitches during the switching-on/switching-off cycles,

That's a pretty good idea, I'd just have used a scope.
Safety devices hinder evolution
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf

 

- - -
-->