I'm waiting some time for a real-world event (e.g. push a button for 3 seconds) on an AVR or STM32 MCU, and I have trouble with code like:
#define PRESS_BUTTON
int waiting = 0;
int t_ms = 0; // time counter
//...
int main(void)
{
while(1)
{
waiting = t_ms + 3000; // waiting button 3 sec
while ((t_ms < waiting) && (!PRESS_BUTTON)) // infinite loop
{}
printf("out"); // not printed
waiting = t_ms = 0;
}
}
ISR( TIMER0_OVF_vect ) // timer interrupt
{
t_ms++;
}
But if I add a printf() inside the while loop, it works!
The same thing happens if I use a do...while loop either. What is causing this?
You need to declare t_ms with volatile
volatile int t_ms =0;
Volatile tells the compiler that the variable may be changed due to external factors, and because of this the compiler will never assume it will stay the same.
In other words, it will force the compiler to check every loop to see if t_ms has changed instead of assuming it will never change.
Related
Using a 8-bit AVR micro, I arrived to a simple situation which might not be that easy to solve.
Consider the following snippet:
static volatile uint8_t counter;
//fires often and I need all the values of the counter.
void isr(void) {
counter++;
}
int main (void) {
while(1) {
send_uart(counter);
counter = 0;
delay_ms(1000); //1 sec pause
}
return 0;
}
1.) It can happen that send_uart is followed by an isr which increases the counter, and then the next statement zeroes it out.
Therefore I'll miss one data from the counter.
2.) If I use ATOMIC_BLOCK(ATOMIC_RESTORESTATE) in the main fn, I can avoid the problems declared in (1), but it can happen that I miss an ISR because in this case INTs are disabled for a short time.
Is there a better way to pass information from the main fn to ISR?
If the counter is sampled rather than reset, there won't be any timing issues. Increments happening while sending will be accounted for in the next iteration. The unsigned data type of the counter variables will guarantee well-defined overflow behavior.
uint8_t cs = 0; // counter sample at time of sending
uint8_t n = 0; // counter as last reported
while (1) {
cs = counter; // sample the counter
send_uart((uint8_t)(cs - n)); // report difference between sample and last time
n = cs; // update last reported value
delay_ms(1000);
}
I am using stm32 timer interrupt.
File 1: (interrupt function)
static int flag = 50;
void timer_inter_handler(){
//...
flag = some changes integer;// not 0, it is changed
#if 0 // **** note
printf("%d\r\n", flag);
#endif
}
int get_flag(){
return flag;
}
File 2: (other file)
int task1(){
static int fccount = 0;
while(1){
fccount++;
if ( fccount%50000 == 0 )
{
printf("%d\r\n", get_flag());
// ...
}
}//while(1)
}
Note:
If this on, task1 could print correct flag data changes. But if it is off, task1 could only print default 50value during system initialization for a couple of time. Then all the flag data printed is 0.
I do not know why this happen. No other place update the var flag. Any clue?
What you need is:
volatile static int flag = 50;
The volatile keyword tells the compiler that the flag variable can change in an interrupt. If the keyword is not there, then the flag variable will not be able to change. That is why you don't see it changing. The #if 0 printf(...) might be forcing the compiler to access the variable.
I found the problem.The problem is inside timer interrupt, hal_adc_start(read and stop), by reading adc value, the timer interrupt routine is hold.
Once I move hal_adc_read, it works fine.
For the last two days i wrote a program that in basic terms generates a fairly accurate user adjustable pulse signal (both frequency and duty cycle adjustable). It basically uses the micros() function to keep track of time in order to pull low or high the 4 digital output channels.
These 4 channels need to have a phase difference of 90 degrees (think a 4cyl engine) always. In order for the user to change settings an ISR is implemented which returns a flag to the main loop to re-initialise the program. This flag is defined as a boolean 'set4'. When it is false a 'while' statement in the main loop will run the outputs. When it is true an 'if' statement will perform the necessary recalculations and reset the flag so that the 'while' statement will resume.
The program works perfectly with the initial values. Phase is perfect. However when the ISR is called and comes back to the main loop, from how i understand it resumes the program in the 'while' statement from where was originally interrupted, until it finishes and re-checks the flag 'set4' to see it is now true and it should stop.
Then, even though the 'if' statement afterwards resets and re-calculates all the necessary variables the phase between these 4 output channels is lost. Tested manually i see depending on which time the ISR is called it will give different results, usually having all 4 output channels synchronised together!
This happens even though i might don't change any values (thus the 'if' routine resets the variables to exactly the same ones when you first power up the arduino!). However, if i comment out this routine and just leave the line which resets the flag 'set4' the program will continue normally like nothing never happened!
I'm pretty sure that this is somehow caused because of the micros() timer because the loop will be resumed from where the ISR was called. I've tried to do it differently by checking and disabling for interrupts using cli() and sei() but i couldn't get it to work because it will just freeze when the arguments for cli() are true. The only solution that i can think of (i've tried everything, spend the whole day searching and trying out stuff) is to force the ISR to resume from the start of the loop so that the program may initialize properly. Another solution that comes to mind is to maybe reset the micros() timer somehow..but this would mess up the ISR i believe.
To help you visualise what is going on here's a snip of my code (please don't mind the 'Millis" name in the micros variables and any missing curly brackets since it is not pure copy-paste :p):
void loop()
{
while(!set4)
{
currentMillis = micros();
currentMillis2 = micros();
currentMillis3 = micros();
currentMillis4 = micros();
if(currentMillis - previousMillis >= interval) {
// save the last time you blinked the LED
previousMillis = currentMillis;
// if the LED is off turn it on and vice-versa:
if (ledState == LOW)
{
interval = ONTIME;
ledState = HIGH;
}
else
{
interval = OFFTIME;
ledState = LOW;
}
// set the LED with the ledState of the variable:
digitalWrite(ledPin, ledState);
}
.
.
//similar code for the other 3 output channels
.
.
}
if (set4){
//recalculation routine - exactly the same as when declaring the variables initially
currentMillis = 0;
currentMillis2 = 0;
currentMillis3 = 0;
currentMillis4 = 0;
//Output states of the output channels, forced low seperately when the ISR is called (without messing with the 'ledState' variables)
ledState = LOW;
ledState2 = LOW;
ledState3 = LOW;
ledState4 = LOW;
previousMillis = 0;
previousMillis2 = 0;
previousMillis3 = 0;
previousMillis4 = 0;
//ONTIME is the HIGH time interval of the pulse wave (i.e. dwell time), OFFTIME is the LOW time interval
//Note the calculated phase/timing offset at each channel
interval = ONTIME+OFFTIME;
interval2 = interval+interval/4;
interval3 = interval+interval/2;
interval4 = interval+interval*3/4;
set4=false;
}
}
Any idea what is going wrong?
Kind regards,
Ken
The problem is here:
previousMillis = 0;
previousMillis2 = 0;
previousMillis3 = 0;
previousMillis4 = 0;
All if statement will be true on the next loop.
Try with:
previousMillis = micros();
previousMillis2 = micros();
previousMillis3 = micros();
previousMillis4 = micros();
Where does this return statement return to if it is inside this infinite while(1) loop? More importantly, I had no idea that a while(1) loop could be broken???
void __attribute__((interrupt, no_auto_psv)) _T3Interrupt(void)
{
int count;
IFS0bits.T3IF = 0; // clear Tmr3 interrupt flag
StopMotor();
IEC0bits.ADIE = 0; // disable ADC interrupt
IEC0bits.CNIE = 0; // disable CN interrupt
IEC0bits.T3IE = 0; // disable Tmr3 interrupt
T3CONbits.TON = 1; // restart tmr3
count = 0;
while (1)
{
if (IFS0bits.T3IF)
{
IFS0bits.T3IF = 0; // clear flag
if (count++ >= RESTART_COUNT)
{
IEC0bits.ADIE = 1; // enable ADC interrupt
IEC0bits.CNIE = 1; // enable CN interrupt
T3CONbits.TON = 0; // stop tmr3
IEC0bits.T3IE = 1; // enable Tmr3 interrupt
return;
}
}
}
return;
}
All return statements will return to wherever the function was called from, regardless of where they are located within the function.
For instance, if I wrote:
int main()
{
_iT3Interrupt();
}
Then the return statement in _iT3Interrupt will revert control flow back to main.
Also, any loop can be exited (even if the condition is 1, true, or some equivalent) with any of the following constructs:
break; //exits the loop
return; //exits the function, thus ending the loop
goto <label-outside-loop>; //self-explanatory
exit(); abort(); //exits the program. Bit of a stretch to say that this ends the loop...
And in C++, throw, which will unwind the stack until it reaches a corresponding catch, thus exiting the function. The C setjmp and longjmp functions may also be applicable here, but I don't know enough C to be certain of their usage.
There are multiple ways to get out of a loop with return break goto
with your snippet if IFS0bits.T3IF != 0 then it will eventually break out of the loop when count >= RESET_COUNT. After that it will return to where the function was called from.
To answer your second question, while(1) is more like while(true). Therefore, it keeps on looping until it encounters a break.
When a function is called the address the function (or ISR) is called from is put on the top of the stack.
The execution of return will end the function (or ISR). The programm counter (PC) is updated with this address, so the program flow could continue with the statement following the calling address.
How would I output text one letter at a time like it's typing without using Sleep() for every character?
Sleep is the best option, since it doesn't waste CPU cycles.
The other option is busy waiting, meaning you spin constantly executing NoOps. You can do that with any loop structure that does absolutely nothing. I'm not sure what this is for, but it seems like you might also want to randomize the time you wait between characters to give it a natural feel.
I would have a Tick() method that would loop through the letters and only progress if a random number was smaller than a threshold I set.
some psuedocode may look like
int escapeIndex = 0;
int escapeMax = 1000000;
boolean exportCharacter = false;
int letterIndex = 0;
float someThresh = 0.000001;
String typedText = "somethingOrOther...";
int letterMax = typedText.length();
while (letterIndex < letterMax){
escapeIndex++;
if(random(1.0) < someThresh){
exportCharacter = true;
}
if(escapeIndex > escapeMax) {
exportCharacter = true;
}
if(exportCharacter) {
cout << typedText.charAt(letterIndex);
escapeIndex = 0;
exportCharacter = false;
letterIndex++;
}
}
If I were doing this in a video game lets say to simulate a player typing text into a terminal, this is how I would do it. It's going to be different every time, and it's escape mechanism provides a maximum time limit for the operation.
Sleeping is the best way to do what you're describing, as the alternative, busy waiting, is just going to waste CPU cycles. From the comments, it sounds like you've been trying to manually hard-code every single character you want printed with a sleep call, instead of using loops...
Since there's been no indication that this is homework after ~20 minutes, I thought I'd post this code. It uses usleep from <unistd.h>, which sleeps for X amount of microseconds, if you're using Windows try Sleep().
#include <stdio.h>
#include <unistd.h>
void type_text(char *s, unsigned ms_delay)
{
unsigned usecs = ms_delay * 1000; /* 1000 microseconds per ms */
for (; *s; s++) {
putchar(*s);
fflush(stdout); /* alternatively, do once: setbuf(stdout, NULL); */
usleep(usecs);
}
}
int main(void)
{
type_text("hello world\n", 100);
return 0;
}
Since stdout is buffered, you're going to have to either flush it after printing each character (fflush(stdout)), or set it to not buffer the output at all by running setbuf(stdout, NULL) once.
The above code will print "hello world\n" with a delay of 100ms between each character; extremely basic.