2018-09-19 07:46 PM
I am using a STM32F072 microcontroller with a matrix keypad.
The matrix pins are essentially bit banged, using ODR mask commands.
When I set up a timer(doesn't matter which one) and I use the timers interrupt (update and a single capture compare) to toggle a pin (manually) it seems to be late processing some of the capture compare and update interrupts.
This results in a waveform that looks like this:
Even weirder it doesn't appear to have anything to-do with the speed of the interrupt, as I get the same result at 100KHz as I do 1KHz (as seen above).
The code to setup the timer is as follows:
CR1().UDIS() = true; //Disable updates
CR1().CEN() = false; //Disable counter
CR1().CMS() = 0; //Edge aligned mode
CR1().DIR() = 0; //Up-Counting
CR1().OPM() = false;
CR1().URS() = true; //Only update on overflow or underflow or DMA request
CR2().CCDS() = false;
CR2().CCPC() = false;
auto const prescales = CalculatePrescale(System::SystemClock_t::Timer(), pFrequency);
ARR() = prescales.ARR;
CCR1() = prescales.ARR / 2;
PSC() = prescales.PSC;
CR1().ARPE() = true; //Buffered auto-reload
DIER().UIE() = true;
DIER().CC1IE() = true;
SR().Clear(); //Clears interrupt flags
CR1().UDIS() = false; //Enable update
CR1().CEN() = true; //Enable counter
In the interrupt service routine I simply clear the flags that were set. The interrupt code is as follows:
static void Interrupt()
{
if (SR().UIF())
{
SR().UIF() = false;
//SET PIN LOW
}
if (SR().CC1IF())
{
SR().CC1IF() = false;
//SET PIN HIGH
}
}
The glitch only happens when the keypad is enabled.
The keypad only does GPIO pin reads, sets and clears,
The keypad sampling is constantly called from the main loop, not an interrupt so I don't think priority has anything to do with this issue. It doesn't disable interrupts or anything like that.
I don't see reducing the keypad sample speed as a real solution as I suspect that would only make the problem less frequent.
I have now confirmed this, the issue is the same, just much less frequent, it needs to go away entirely.
Solved! Go to Solution.
2018-09-19 10:11 PM
SOLUTION:
An AND/OR operation on the ODR register ultimately ends up being three instructions, a read, a modify and then a write.
This evaluates to multiple instructions.
When an interrupt is called and another pin is set which is on the port the copied value from the first operation is made redundant as the registers value is changed. This means after the interrupt returns the change from within the interrupt is undone by the assignment of the modified copy of the port.
Example:
In main loop:
PORTA &= MASK1
In interrupt:
PORTA &= MASK2
This compiles to something like:
In main loop:
COPY1 = PORTA;
COPY1 = COPY1 & MASK1; //KEY POINT 1
PORTA = COPY1;
In Interrupt:
COPY2 = PORTA;
COPY2 = COPY2 & MASK1;
PORTA = COPY2;
If an interrupt occurs at KEY POINT 1 then on a single core the instructions look like this:
COPY1 = PORTA;
COPY1 = COPY1 & MASK1; //Enter interrupt after here
COPY2 = PORTA;
COPY2 = COPY2 & MASK1;
PORTA = COPY2; //Return from interrupt after here
PORTA = COPY1;
Note that PORTA is immediately reassigned to the value of COPY1 when the interrupt returns. This is what the issue was here.
2018-09-19 08:49 PM
Not sure what library/language you're using here, but the actions on TIM->SR must NOT use RMW
TIM->SR &= ~1; // IS WRONG
TIM->SR = ~1; // IS CORRECT WAY TO CLEAR BIT ZERO
Using RMW will result in loss of interrupts as it creates an window where you inadvertently clear something by ANDing it with ZERO. This is a peripheral register, is does not act like a memory cell.
2018-09-19 09:20 PM
Thanks, trying that now, although internally the library performs an assignment but I may have messed it up in a way I haven't figured out...
Attempt 1, HAL Code simple clear (still glitches):
TIM1->SR = 0;
Attempt 2, HAL Code (still glitches):
static void Interrupt()
{
if (SR().UIF())
{
//Set pin false
TIM2->SR = ~TIM_SR_UIF;
}
if (SR().CC1IF())
{
//Set pin true
TIM2->SR = ~TIM_SR_CC1IF;
}
}
The language is C++, the library is an attempt to get rid of the runtime bloat in HAL by doing all the runtime checks it performs at compile-time.
EDIT:
What I cannot understand is why the matrix keypad being active makes such a difference. It only performs operations on the ODR. I could try the BSRR and BRR but I don't understand why ODR is at all interfering.
WEIRD SOLUTION?
Changing the GPIO pin set and clear commands to use BSRR and BRR fixed the issue WHY?:\
This answer hints at why, still puzzled:
https://electronics.stackexchange.com/questions/99095/stm32f0-gpiox-odr-vs-gpiox-bsrr
2018-09-19 10:04 PM
TIM1->SR = 0; // This clears ALL interrupts indiscriminately
2018-09-19 10:11 PM
SOLUTION:
An AND/OR operation on the ODR register ultimately ends up being three instructions, a read, a modify and then a write.
This evaluates to multiple instructions.
When an interrupt is called and another pin is set which is on the port the copied value from the first operation is made redundant as the registers value is changed. This means after the interrupt returns the change from within the interrupt is undone by the assignment of the modified copy of the port.
Example:
In main loop:
PORTA &= MASK1
In interrupt:
PORTA &= MASK2
This compiles to something like:
In main loop:
COPY1 = PORTA;
COPY1 = COPY1 & MASK1; //KEY POINT 1
PORTA = COPY1;
In Interrupt:
COPY2 = PORTA;
COPY2 = COPY2 & MASK1;
PORTA = COPY2;
If an interrupt occurs at KEY POINT 1 then on a single core the instructions look like this:
COPY1 = PORTA;
COPY1 = COPY1 & MASK1; //Enter interrupt after here
COPY2 = PORTA;
COPY2 = COPY2 & MASK1;
PORTA = COPY2; //Return from interrupt after here
PORTA = COPY1;
Note that PORTA is immediately reassigned to the value of COPY1 when the interrupt returns. This is what the issue was here.
2018-09-19 10:41 PM
@Clive Two.Zero
You are right of course, was just test code.
In the test scenario it was guaranteed that:
This means that only one of the flags are possible at any time meaning that indiscriminate clearing of flags wasn't any different to clearing individual flags. In the general case not a great idea to clear everything, but for such a specific test it made no difference.
2018-09-20 01:48 AM
This is why the GPIO on STM32 have the BSRR register.
JW