Struggling to implement tickless support for FreeRTOS on an xmega256a3 - c

I'm struggling to get tickless support working for our xmega256a3 port of FreeRTOS. Looking around, trying to understand under the hood better, I was surprised to see the following line in vTaskStepTick():
configASSERT( xTicksToJump <= xNextTaskUnblockTime );
I don't have configASSERT turned on, but I would think that if I did, that would be raising issues regularly. xTicksToJump is a delta time, but xNextTaskUnblockTime, if I read the code correctly, is an absolute tick time? Did I get that wrong?
My sleep function, patterned after the documentation example looks like this:
static uint16_t TickPeriod;
void sleepXmega(portTickType expectedIdleTime)
{
TickPeriod = RTC.PER; // note the period being used for ticking on the RTC so we can restore it when we wake up
cli(); // no interrupts while we put ourselves to bed
SLEEP_CTRL = SLEEP_SMODE_PSAVE_gc | SLEEP_SEN_bm; // enable sleepability
setRTCforSleep(); // reconfigure the RTC for sleeping
while (RTC.STATUS & RTC_SYNCBUSY_bm);
RTC.COMP = expectedIdleTime - 4; // set the RTC.COMP to be a little shorter than our idle time, seems to be about a 4 tick overhead
while (RTC.STATUS & RTC_SYNCBUSY_bm);
sei(); // need the interrupt to wake us
cpu_sleep(); // lights out
cli(); // disable interrupts while we rub the sleep out of our eyes
while (RTC.STATUS & RTC_SYNCBUSY_bm);
SLEEP.CTRL &= (~SLEEP_SEN_bm); // Disable Sleep
vTaskStepTick(RTC.CNT); // note how long we were really asleep for
setRTCforTick(TickPeriod); // repurpose RTC back to its original use for ISR tick generation
sei(); // back to our normal interruptable self
}
If anyone sees an obvious problem there, I would love to hear it. The behavior it demonstrates is kind of interesting. For testing, I'm running a simple task loop that delays 2000ms, and then simply toggles a pin I can watch on my scope. Adding some printf's to my function there, it will do the first one correctly, but after I exit it, it immediately reenters, but with a near 65535 value. Which it dutifully waits out, and then gets the next one correct again, and then wrong (long) again, alternating back and forth.

Related

Trouble programming AVR to interpret input from Arduino rotary encoder module

I've been trying to program my ATtiny817-XPRO to interpret input data from a rotary encoder (the Arduino module), however I'm having some trouble and can't seem to figure out what the problem is. What I'm trying to do is essentially program a digital combination lock that blinks a red LED every time the rotary encoder is rotated one "tick" (in either direction), and blinks a green LED once the right "combination" has been detected. It's a little bit more involved than that, so when I ran into trouble upon testing my code, I decided to write a simple method to help me troubleshoot/debug the problem. I've included it below:
void testRotaryInput(){
if(!(PORTC.IN & 0b00000001)){ // if rotary encoder is turned clockwise
PORTB.OUT = 0b00000010; // turn on green LED
}
else if(!(PORTC.IN & 0b00000010)){ // if rotary encoder is turned CCW
PORTB.OUT = 0b00000001; // turn on blue LED
}
else{ // if rotary encoder remains stationary
PORTB.OUT = 0b00000100; // turn on red LED
}
RTC.CNT = 0;
while(RTC.CNT<16384){} // wait 500ms
PORTB.OUT = 0x00; // turn LED off
while(RTC.CNT<32768){} // wait another 500ms
}
int main(void)
{
PORTB.DIR = 0xFF; // PORT B = output
PORTC.DIR = 0x00; // PORT C = input
RTC.CTRLA = RTC_RTCEN_bm; // Enable RTC
PORTB.OUT = 0x00; // Ensure all LEDs start turned off
// ^(not necessary but I do it just in case)^
//testLED(); <-- previous test I used to make sure each LED works upon start-up
while(1)
{
testRotaryInput();
}
}
The idea here is that whichever output line arrives at the AVR first should indicate which direction the rotary encoder was rotated, as this dictates the phase shift between the two signals. Depending on the direction of rotation (or lackthereof), a red/green/blue LED will blink once for 500ms, and then the program will wait another 500ms before listening to the rotary encoder output again. However, when I run this code, the LED will either continuously blink red for awhile or green for awhile, eventually switching from one color to the other with the occasional (single) blue blink. This seems completely random each time, and it seems to completely ignore any rotation I apply to the rotary encoder.
Things I've done to troubleshoot:
Hooked up both outputs of the rotary encoder to an oscilloscope to see if there's any output (everything looked as it should)
Used an external power supply to power the rotary encoder, as I was only reading 1.6V from the 5.0V VCC pin on my ATtiny817-XPRO when it was connected to that (I suspect this was because the LEDs and rotary encoder probably draw too much current)
Measured the voltage from said power supply to ensure that the rotary encoder was receiving 5.0V (I measured approx. 4.97V)
Checked to make sure that the circuitry is correct and working as it should
Unfortunately, none of these things eliminated the problem at hand. Thus, I suspect that my code may be the culprit, as this is my first attempt at using a rotary encoder, let alone interpreting the data generated by one. However, if my code looks like it should work just fine, I'd appreciate any heads-up on this so that I can focus my efforts elsewhere.
Could anyone shed light on what may be causing this issue? I don't think it's a faulty board because I was using these pins two nights ago for a different application without any problems. Also, I'm still somewhat of a newbie when it comes to AVRs, so I'm sure my code is far from being as robust as it could be.
Thanks!
Encoders can behave in various strange ways. You'll have signal bounces like with any switch. You'll have cases where several inputs may be active at once during a turn. Etc. Therefore you need to sample them periodically.
Create a timer which polls the encoder every 5ms or so. Always store the previous read. Don't accept any change to the input as valid until it has been stable for two consecutive reads. This is the simplest form of a digital filter.
Which input is shorted does not show you what direction the encoder was turned. But the order in which they were shorted does.
Normally, rotary encoders have two outputs which are shorted to the ground pin: first shorted, then second shorted, then first released, then second released - this full sequence happened between each click. (Of course there are encoders which have additional "click" in the middle of the sequence, or have no clicks at all, but most of them do like described above).
So, generally speaking, each "CLICK!" movement you may consider as 4 phases:
0. Both inputs are released (high) - default position
1. Input A is shorted to ground (low), input B is released (high)
2. Both inputs are shorted (low)
3. Input A is released (high), B is shorted (low).
Rotation in one direction is a passage thru phases 0-1-2-3-0. Another direction is 0-3-2-1-0. So, whatever direction the encoder is rotated, both inputs will be shorted to ground at some particular moment.
As you can see from the picture above, usually the bouncing happens only at one of inputs. So, you may consider the bouncing as jumping between two adjacent phases, what makes the debounce much simpler.
Since those phases are changes very fast you have to pool input pins very fast, may be 1000 times per second, to handle fast rotations.
Code to handle the rotation may be as follows:
signed int encoder_phase = 0;
void pull_encoder() {
int phase = ((PORTC.IN & (1 << INPUT_A_PINNO)) ? 0 : 1)
^ ((PORTC.IN & (1 << INPUT_B_PINNO)) ? 0 : 0b11);
// the value of phase variable is 0 1 2 or 3, as described above
int phase_shifted = (phase - encoder_phase) & 3;
if (phase_shifted == 2) { // jumped instantly over two phases - error
encoder_phase = 0;
} else if (phase_shifted == 1) { // rotating forward
encoder_phase++;
if (encoder_phase >= 4) { // Full cycle
encoder_phase = 0;
handle_clockwise_rotation();
}
} else if (phase_shifted == 3) { // rotating backward;
encoder_phase--;
if (encoder_phase <= -4) { // Full cycle backward
encoder_phase = 0;
handle_counterclockwise_rotation();
}
}
if (phase == 0) {
encoder_phase = 0; // reset
}
}
As others have noted, mechanical encoders are subject to bouncing. You need to handle that.
The most simple way to read such an encoder would be to interpret one of the outputs (e.g. A) as a 'clock' signal and the other one (e.g. B) as the direction indicator.
You wait for a falling (or rising) edge of the 'clock' output, and when one is detected, immediately read the state of the other output to determine the direction.
After that, include some 'dead time' during which you ignore any other edges of the 'clock' signal which occur due to the bouncing of the contacts.
Algorithm:
0) read state of 'clock' signal (A) and store ("previous clock state")
1) read state of 'clock' signal (A) and compare with "previous clock state"
2) if clock signal did not change e.g. from high to low (if you want to use the falling edge), goto 1).
3) read state of 'direction' signal (B), store current state of clock to "previous clock state"
4) now you know that a 'tick' occurred (clock signal change) and the direction, handle it
5) disable reading the 'clock' signal (A) for some time, e.g. 10ms, to debounce; after the dead time period has elapsed, goto 1)
This approach is not time critical. As long as you make sure you poll the 'clock' at least twice as fast as the shortest time between the change of signal A and the corresponding change of signal B (minus bouncing time of A) you expect to see (depends on maximum expected rotation speed) it will work absolutely reliably.
The edge detection of the 'clock' signal can also be performed by a pin change interrupt which you just disable for the dead time period after the interrupt occurred. Handling bouncing contacts via a pin change interrupt is however generally not recommended because the bouncing (i.e. (very) fast toggling of a pin, can be pulses of nanoseconds duration) may confuse the hardware.

How to implement twice button interrupt PIC

I'm working with a 16F1703 PIC mcu, and I want to begin a 7segment lcd cycle (0-9) loop at a touch of a button(A1), after that if I touch the button(A1) twice, I want the Pic to enter in sleep mode.
To do that, I implemented this:
#include <test_interrupt.h>
byte const DataExit[10]={0b01000100,
0b01011111,
0b01100010,
0b01001010,
0b01011001,
0b11001000,
0b11000000,
0b01011110,
0b01000000,
0b01001000};
byte const bitMask[8]={1,2,4,8,16,32,64,128};
//show seven numbers
void segmentCycle(void){
int i, j;
for(i=0;i<10;i++){
for (j=0; j<8;j++){
output_low(CLK);
output_bit(DATA,DataExit[i] & bitMask[j]);
output_high(CLK);
}
delay_ms(7000);
output_low(CLR);
delay_ms(6000);
output_high(CLR);
}
}
#INT_IOC
void IOC_isr(void)
{
segmentCycle();
sleep();
}
void main()
{
port_a_pullups(0x02);
enable_interrupts(INT_IOC_A1);
enable_interrupts(INT_IOC_A1_H2L);
enable_interrupts(GLOBAL);
while(TRUE);
}
For now, if I touch the button, sometimes it starts, otherwise It don't.
What do you suggest?
I'm using ccs compiler.
Your code lacks a proper debounce algorithm and your circuit design is probably flawed. Hooking a button to an interrupt is a waste of a valuable resource, especially if it lacks a debounce circuit. That aside for now, your ISR is going off and doing at least 13000ms of work (well "delay")! ISR's should be short and fast. When they happen, they interrupt whatever code is running at the time, and in the absence of any hard/soft debounce mechanisms, are likely to fire many times per button press (put a scope on that button). That means you're ISR routine might be entered many times before it ever exits from the first call, but even that depends on pin configurations we can only guess at because the relevent code is missing from your OP.
Normally, you'd have a main loop that does whatever work needs to happen and the ISR's simply signal state changes via flags, counters or enumerations. When the main loop detects a state change, it calls whatever function(s) handle that change. In your case, it probably needs to check the current time and the last time the button was pressed, and verify that a minimum period has elapsed (500ms is usually good enough on pin with reasonable pull-up). If not enough time has passed, it resets the flag, otherwise it does the needed work.
See page 72 of the device spec. and take note that there are multiple interrupt sources and the ISR is responsible for determining which source caused it to fire. Your code doesn't look at the interrupt flags, nor does it clear the previous interrupt before exit, so you're never going to see more than one interrupt from any particular source.
With a little bit of searching, you should be able to find free code written to work on your specific PIC chip that handles button debounce. I recommend that you find and study that code. It will make for a very good starting point for you to learn and evolve your project from.
Looks like your hangup is that you want the PIC to be sleeping until the button is pressed. I would do something like this, using CCS C Compiler:
#include <16f1703.h>
#use delay(int=8MHz)
#use fast_io(A)
#include <stdbool.h>
#include <stdint.h>
bool g_isr_ioc_flag = 0;
uint8_t g_isr_ioc_porta = 0;
#int_ioc
void isr_ioc(void)
{
if (!g_isr_ioc_flag) //only trap first IOC
{
g_isr_ioc_flag = 1;
g_isr_ioc_porta = input_a();
}
}
void main(void)
{
uint8_t debounced_a;
set_tris_a(2);
port_a_pullups(0x02);
enable_interrupts(INT_IOC_A1);
enable_interrupts(INT_IOC_A1_H2L);
enable_interrupts(GLOBAL);
for(;;)
{
sleep();
if (g_isr_ioc_flag)
{
// cheap debounce. bit in debounced_a set if pin has been low
// for at least 72ms.
delay_ms(72);
debounced_a = ~g_isr_ioc_porta & ~input_a();
g_isr_ioc_flag = 0;
input_a(); // clear IOC flags left by race condition
if (bit_test(debounced_a, 1))
{
// TODO: user code - handle RA1 press
}
}
}
}
Normally a debounce routine would have had to poll the pin to see if it went low to start debounce timing - but in your case the interrupt on change (IOC) ISR does some of that work for us.
The CCS function input_a() automatically clears the relevant IOC flag bits in IOCAF. That means calling input_a() will clear the ISR flag until the next change.
This design lets you handle other wakeup sources in the main loop. Be aware that any other peripheral ISR or the WDT will wakeup the PIC.

NVIC_SystemReset() stuck in while loop (STM32F302VB)

I'm currently developing on a STM32F302VB and I need to perform a software reset. On all my previous projects (with STM32F427 and STM32F030C8), I've always used the NVIC_SystemReset() function successfully. But for some reason it won't work with this chip.
The implementation is in CMSIS core_cm4.h and is as follows:
__STATIC_INLINE void NVIC_SystemReset(void)
{
__DSB(); /* Ensure all outstanding memory accesses included buffered write are completed before reset */
SCB->AIRCR = ((0x5FA << SCB_AIRCR_VECTKEY_Pos) |
(SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) |
SCB_AIRCR_SYSRESETREQ_Msk); /* Keep priority group unchanged */
__DSB(); /* Ensure completion of memory access */
while(1); /* wait until reset */
}
The function is called and all instructions are executed, but it gets stuck in the while loop, and reset never happens. I then have to reset it via JTAG to get it out of that state.
I checked the programming manual and the implementation seems fine (not surprising since it works perfectly on F4 and F0).
I really don't know what the problem might be, does someone have an idea what's going on?
Edit: The function is still not working but as a workaround, after the function gets stuck, I pull down the nRST pin and then up. It's ugly, but it works for now. I would rather do it all in software though.
Tony K was right in his comment, the nRST pin was indeed being pulled high externally, because of a routing mistake.
And contrary to what I thought, the nRST pin is taken into account even in a software reset: the reference manual says: "[Reset] sources act on the NRST pin and it is always kept low during the delay phase", so I should have known!
Removing the pull-up did the trick, the NVIC_SystemReset() function now works as expected!
Thank you very much!

PIC32 WDT reset not working on some PIC32

I have a PIC32 reset function:
void reset_cpu(void)
{
WDTCON=0x8000;
EnableWDT(); // enable the WDT
ClearWDT();
while(1){};
}
It works on a PIC32MX360F512L but not on a PIC32MX695F512L. It just spins forever. Can anyone tell me why, or suggest another way to reset my processor?
If you are using plib.h, you can simply call this function:
void reset_cpu(void)
{
SoftReset();
while(1){};
}
This has the advantage to trigger an instant reset. From reset.h:
How it works: The following steps are performed by this function:
Step 1 - Execute "unlock" sequence to access the RSWRST register.
Step 2 - Write a '1' to RSWRST.SWRST bit to arm the software reset.
Step 3 - A Read of the RSWRST register must follow the write. This action triggers the software reset, which should occur on the next
clock cycle.
Bear in mind plib is obsolete and will soon be removed from MPLAB XC32. Worth considering Harmony for new designs: http://www.microchip.com/mplabharmony
Nothing immediately stands out to me when looking at the datasheets for both of the microcontrollers. However, I do have a couple of suggestions.
First, in your function you are doing the following:
WDTCON=0x8000;
EnableWDT();
If you look at plib.h you will see that it refers to wdt.h. In wdt.h you can see that EnableWDT() is simply a macro that expands to the following:
WDTCONSET = _WDTCON_WDTCLR_MASK
Where the mask is 0x00008000. Basically, you are performing the same operation twice. Just let the macro take care of enabling it.
Also, since you are using the watchdog to reset your device, there is no need to clear the watchdog. ClearWDT() just resets the watchdog and makes your while(1) loop run longer. So, I would write your function like this:
void reset_cpu(void)
{
EnableWDT();
while(1){};
}
Finally, I would recommend taking a look to ensure that you have the correct processor selected in your IDE. I am not certain that this would cause your problem, but if you had the PIC32MX360F512L selected and tried running it on the PIC32MX695F512L, you could end up with the wrong register definitions (assuming that you are using #include "xc.h").
I would also check on how you are setting your device configuration bits. It is possible to set a very long timeout on the watchdog.

Temporarily disable timer interrupt

I'm working on an embedded project in C on a stm32f4xx uC.
I have a portion of a code that does an loop-operation XYZ continuously, and from time to time a TIM4 interrupt changes some global parameters and causes the operation XYZ to restart.
code is something like this:
for (;;) {
//line A
XYZ;
//line B
}
XYZ is a complex operation involving tranfers of data between buffers and others.
The TIM4 interrupt handler does this: stops XYZ & changes some globals that afect XYZ operations.
So basically I want XYZ to execute repeatedly and TIM4 interrupt to stop XYZ, change the parameters and then the loop must restart by restarting XYZ with the new global parameters.
PROBLEM IS: Since XYZ has many instructions, TIM4 IRQ may come right in the middle of it and, after the IRQHandler changes the globals, the operations resume from the middle of XYZ which ruins the program.
MY INITIAL SOLUTION: Disable interrupts on line A with __disable_irq() and restore them on line B with __enable_irq()
Fails because the XYZ complex operation must use other interrupts (other than TIM4).
NEXT SOLUTION Disable only TIM4 interrupt on line A with:
TIM_ITConfig(TIM4, TIM_IT_Update , DISABLE)
and enable it back on line B with:
TIM_ITConfig(TIM4, TIM_IT_Update , ENABLE)
Fails because I am losing the interrupt: when the int is restored, the interrupt that arrived during XYZ is ignored. This is a big problem (one of the reasons is that TIM4 IRQHandler changes the globals and then activates the TIM4 again to give an interrupt later, I do this because the period between interrupts varies).
Can anyone give me a solution to this problem? Is there a better way to disable/restore TIM4 IRQ and NOT lose any interrupt?
You could operate on a copy of the global variables and swap in the new value from the interrupt once you're done with XYZ.
It's not clear from the question whether you need to stop processing of XYZ immediately when the globals change, or if you can wait till XYZ finishes processing to swap in new copies of the variables. I'll operate under the assumption that you need to break out of processing XYZ but it's easy enough to not.
Code would look something like this:
volatile int x;
int x_prime;
int main(void)
{
while(1)
{
//copy in new global parameter value
x_prime = x;
while(1)
{
//do stuff with x_prime
if (x_prime != x)
{
break;
}
//do more stuff with x_prime
if (x_prime != x)
{
break;
}
}
}
}
// interrupt handler
void TIM_IT_Update(void)
{
x++;
}
The break patterns assume that you're not changing x_prime. If you need to modify x_prime, you'll need another copy.
Since the interrupt is never disabled, you never have to worry about losing any of them. And since you're operating on a copy of the parameters changed by the interrupt, it doesn't matter if the interrupt changes the parameters in the middle of execution because you're not looking at those values until you make copies.
There's a few options potentially available (I'm not 100% on ARM architecture):
Alter the interrupt priority/level mask register to only mask off TIM4, leaving other interrupts to happen. Hopefully, if TIM4 is fired whilst masked, on restoring the level mask it will remember & fire the ISR.
Mask off interrupts and manually check for the TIM4 interrupt flag being set during XYZ
Break XYZ into smaller sections and only mask off TIM4 when absolutely necessary.
Operate on a copy of the data, optionally checking the TIM4 interrupt flag to decide whether to continue/keep the result or to discard & restart.
Check the time & avoid starting XYZ if TIM4 is likely to fire soon, or only run XYZ N times after TIM4 fires.
When I find myself in a similar situation, where processing may take longer time than the interruption period, I use a FIFO to detach the processing from the incoming data. I.E: TIM4 fills a FIFO. XYZ (or a manager) consume the FIFO and process the data.
(I feel that your design may be wrong, since you shouldn't be using globals to control the data or process flow.
Book reference for study on the matter: Making Embedded Systems: Design Patterns for Great Software)
Before XYZ make a copy of everything from the buffer and work with copies. I believe it's the best way, helped during writing a gps parser.
Have you considered using a RTOS for your system? I realize that would require some restructuring, but it may provide you the task handling flexibility and resolution you need for your system. If you're using a STM32's CubeIDE, enabling, configuring, and getting running with a RTOS is fairly straightforward.

Resources