This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

EK-LM4F120XL: Handling multiple interrupts affecting each other correctly

Part Number: EK-LM4F120XL

Hi, I have the following setup: Wheel-encoders connected to INT_GPIOC and INT_GPIOD  (left wheel interrupts C  and right wheel interrupts D) and INT_TIMER2A interrupt increasing counter counting seconds since init. The purpose is that each time interrupt corresponding to wheel-encoder fires it generates a message containing time since init and the information about the wheel side and directions and send it through UART forward. The problem is that in some cases the time stamps of the events seems to be in the wrong order and this seems to happen every time the TIMER_INTERRUPT_FIRES i.e, one second is elapsed (see example sequence below) . I suspect this is related to interrupts overlapping each other but I'm not sure how to fix it? I would also like to move the UART sending routine (serial_write_message) to main loop away from the interrupt. However in this case I think I should block GPIOC and GPIOD interrupts firing before the serial_write_message_is completed, but not to loose them , i.e., something like below, but I'm not sure if it is possible to queue the interrupts for time of execution in main code? I would appreciate any suggestions to solve the problem.

Pseudo code sending the message through UART in main code instead of interrupt:

while(1) {

  //flag indicating new message 
  if(new_message) {
    //queue interrupt routines generating new_message_string until old message is written to UART  (but does not loose the interrupt)
    block_GPIO_interrupts();
    //new_message_string contents are set in interrupt routine
    serial_write_message(new_message_string);
//old message written new messages can be accepted from interrupts unblock_GPIO_interrupts(); new_message = 0; } }
Example sequence of corrupted events

0 117.908203 0 1
0 117.956008 1 1
0 117.960891 0 1
0 117.999351 1 1
0 118.4722 0 1
0 118.56480 0 1
0 118.59448 1 1
0 118.102294 0 1
0 118.103828 1 1
0 118.156608 0 1
0 118.163978 1 1
0 118.202339 0 1
0 118.206573 1 1

The actual code generating and implementing the interrupts:

static void init_interrupts(){
  
  GPIOPinIntEnable(GPIO_PORTD_BASE, input1);
  GPIOPinIntEnable(GPIO_PORTC_BASE, input2);
  IntEnable(INT_GPIOD);
  IntEnable(INT_GPIOC);
  IntEnable(INT_TIMER2A);
  TimerIntEnable(TIMER2_BASE, TIMER_TIMA_TIMEOUT);
  IntMasterEnable();
  TimerEnable(TIMER2_BASE, TIMER_A);

}

void system_time() {
  	TimerIntClear(TIMER2_BASE, TIMER_TIMA_TIMEOUT);
  	seconds_since_init = seconds_since_init + 1;
}

void IntGPIOd(void){

	if(!GPIOPinRead(GPIO_PORTD_BASE, input1) && left_high){
		return;
	} else if(GPIOPinRead(GPIO_PORTD_BASE, input1) && !left_high) {
		return;
	}

	left_high = GPIOPinRead(GPIO_PORTD_BASE, input1) > 0 ? 0 : 1;
	int wheel = 0;
	int wheel_dir = 1;
	char rover_direction = motor_get_direction();

	if(rover_direction == 'f') {
		wheel_dir = 1;
	} else if(rover_direction == 'b') {
		wheel_dir = 0;
	} else if(rover_direction == 'l') {
			wheel_dir = 0;
	} else if(rover_direction == 'r') {
			wheel_dir = 1;
	}

	GPIOPinIntClear(GPIO_PORTD_BASE, input1);

	float realtime = seconds_since_init + (float)(SysCtlClockGet() -1 - TimerValueGet(TIMER2_BASE, TIMER_A)) / ((float) SysCtlClockGet());
	char message[80];
	int message_len = eb_sprintf(message, "%d %f %d %d\n", 0, realtime, wheel, wheel_dir);
	serial_write_message(message, message_len);
}

  • Nippe,
    Keep your interrupt routines as short as possible: simply record the values you need, and mark a flag for processing later.
    What is the frequency of your interrupts? There is no need to block them if your serial transmission always happens faster than the next interrupt. And if that is not the case, then you have different problems - you would need to process some sort of information internally and transmit just "the current speed", for example, or some other sort of processed data.
    Also, search this forum for "pwm timer readings" or something like that - there are two long and enlightening discussions about the art of measuring interrupt times.
    Regards
    Bruno
  • Hi, thank you for the reply. I have no moved most of the code away from the interrupt routines into main code (shown below) . However I'm still encountering the same phenomena, i.e., every time a new second starts the first few time stamps are incorrect. The time stamps are also strangely truncated, i.e., the number of digits should be 6 but in those cases the number of digits is 4 (or sometimes 5) .  The frequency is in most of the cases > 0.02 seconds . I will try to take a look at those "pwm time readings" discussions if those could give some hints to solve the problem.

    Sample time stamps:

    0 124.869972 0 1
    0 124.882019 1 1
    0 124.912612 0 1
    0 124.922569 1 1
    0 124.963287 0 1
    0 124.979240 1 1
    0 125.4875 0 1
    0 125.15571 1 1
    0 125.53894 0 1
    0 125.72471 1 1
    0 125.97198 0 1
    0 125.108673 1 1
    0 125.148162 0 1
    0 125.161460 1 1
    0 125.189491 0 1
    0 125.194854 1 1
    0 125.241081 0 1
    0 125.252151 1 1
    0 125.282821 0 1
    0 125.284980 1 1
    0 125.332481 0 1

    Main loop:

     while(1){
    
    	  if(sensors_encoder_updated()) {
    		  float realtime = sensors_get_seconds_since_init();
    		  struct EncoderState state = sensors_get_encoder_state();
    		  char message[80];
    		  int message_len = eb_sprintf(message, "%d %f %d %d\n", 0, realtime, state.wheel, state.wheel_dir);
    		  serial_write_message(message, message_len);
    		  sensors_clear_encoder_updated();
    	  }
      }

    Interrupt routines:

    void IntGPIOc(void){
    
    	char rover_direction = motor_get_direction();
    
    	unsigned int wheel_dir = 1;
    	if(rover_direction == 'b' || rover_direction == 'r') {
    		wheel_dir = 0;
    	}
    
    	encoder_state.wheel = 1;
    	encoder_state.wheel_dir = wheel_dir;
    	encoder_state.state_updated = 1;
    
    	GPIOPinIntClear(GPIO_PORTC_BASE, input2);
    }

  • The problem was actually in the eb_sprintf function and it got nothing to do with the interrupts. If the input for the function was something like 21.001212 the zeros at beginning of digits were omitted.
  • Thanks for sharing the problem, Nippe.
    For several reasons, I avoid these transfers of numeric values using text - and this is one of them. Typically prone to conversion errors, and requiring additional processing both before transferring and after receiving. Sometimes it ain't an option (i.e. when decoding GPS NMEA messages), and then you want to use a reliable string to numeric conversion function!
    Cheers
    Bruno