I am using an LPC2378 (ARM7TDMI-S) uC for a project. Timer0 on the uC is being used to measure frequency of a signal on one of the inputs of the uC. It is setup to interrupt on the rising edge of the signal (input capture) as well as interrupt on match register. The match register interrupt is set such that an interrupt occurs every 50mS. In my main code I have a while loop, which will only exit if a variable is set which only happens whenever the match interrupt of timer0 occurs. My ISR looks like this
static __irq __arm void TIMER_handler0(void) {
DWORD T0int;
T0int = T0IR;
if (T0int & IIR_CR0) { // interrupt due to rising edge
T0IR_bit.CR0INT = 1; // acknowledge interrupt
//Do stuff to figure out frequency of input signal
}
if ((T0int & IIR_MR0) || (T0int & IIR_MR1)) { // interrupt due to match
T0IR_bit.MR0INT = 1; // acknowledge interrupt
T0CCR_bit.CAP0I = 0; // turn off both interrupts (capture & match)
T0MCR_bit.MR0I = 0;
T0TCR_bit.CE = 0; // turn off the counter and reset the counter register
T0TC = 0;
timer0busy = 0;
}
VICADDRESS = 0x0;
}
While loop in my main code checks "timer0busy" flag and looks like this
while(timer0busy) {
//Do stuff
}
"timer0busy" flag is set to 1 when the timer0 is enable which is done before this while loop
On some occasions I have noticed that my uC would stop sending output through the UART. On further invertigation I found that it was stuck in the above while loop because "timer0busy" flag never became 0. I did some more testing and figured that this usually happens when the frequency of the input signal (which is measured by timer0) is increased. Now my understanding is that as the frequency of the input signal is increased the interrupts due to rising edge will also increase, but what I cannot figure out is that why does at some point the interrupt due to match register stops happening. The match register is setup to interrupt every 50mS regardless of the frequency of the input signal, and when this interrupt happens, interrupts are disabled and flag is set to 0. For both capture and match register interrupts the ISR is the same. Is the timer overshooting the match value without getting caught?. I don' know how that could happen. What would happen if the rising edge and match happens at the same time?. Could this be the result when that happens
At what frequency approximately does interrupt no longer work?
From what I understand this part of interrupt runs at every rising edge of the signal:
if (T0int & IIR_CR0) { // interrupt due to rising edge
T0IR_bit.CR0INT = 1; // acknowledge interrupt
//Do stuff to figure out frequency of input signal
}
Are you sure it has enough time to run before interrupt is triggered again? I would count clock cycles if you're unsure.
LPC2378 Does not have a Nester VIC, just a regular VIC. What that means is if interrupt A has occurred, and while it is mid-way through running, interrupt B is triggered, regular VIC cannot pause process called by A, run process B, then reenter A. It runs process A to completion, then runs process B.
My guess is that when frequency of your signal is high enough, your system calls interrupt upon interrupt without time to execute them to completion, causing a rapidly growing build up of work until VIC panics.
Try limiting the frequency of interrupts due to rising edge (like every second edge) when frequency gets too high.
Related
I have RX interrupts working just fine, but I wanted to add TX interrupts. I respond to long commands over the UART and don't want to waste cycles waiting for the TX to complete before sending along the next byte. I am trying to enable interrupts, transmit the data that needs to be transmitted, then disable the interrupts until the next TX packet comes along.
This works fine for the FIRST payload that I send out. I see it go out just fine. However as soon as I disable TX Interrupts once, I am never able to enter the ISR again. How on the MSP430 does one enable TX Interrupts on the UART and have it ever get into the ISR again?
Below where you see EUSCI_A_UART_enableInterrupt call, shouldn't this line trigger my ISR every time the interrupt gets enabled? If not, HOW DO I GET BACK INTO THE ISR AGAIN?
Here's the Transmit code:
void UartSendChar(uint8_t tx_char)
{
ring_buffer_put(_rbdTx, &tx_char);
EUSCI_A_UART_enableInterrupt(EUSCI_A1_BASE, EUSCI_A_UART_TRANSMIT_INTERRUPT); // Enable interrupt
}
Here's my ISR:
void EUSCI_A1_ISR(void)
{
int c=-1;
switch(__even_in_range(UCA1IV,USCI_UART_UCTXCPTIFG))
{
case USCI_NONE: break;
case USCI_UART_UCRXIFG:
...
case USCI_UART_UCTXIFG:
// If there's something in the Ring Buffer, transmit it
// If not, then disable TX interrupts until new data gets written.
if (ring_buffer_get(_rbdTx, &c) == 0)
{
EUSCI_A_UART_transmitData(EUSCI_A1_BASE, (uint8_t)c);
}
else
{
EUSCI_A_UART_disableInterrupt(EUSCI_A1_BASE, EUSCI_A_UART_TRANSMIT_INTERRUPT);
}
I figured out the problem. In this particular MSP430, when the interrupt vector is read and it shows a TX interrupt, the TXIFG bit automatically gets cleared by the micro. (How nice of it)
In order to get this ISR to fire again after re-enabling TX interrupts, the TXIFG bit must be manually set back to 1 again, which shows an interrupt pending. That way, when interrupts are enabled after data is shoved into the queue, the TXIFG bit is set, so the ISR executes and ships the data off.
Now my ISR looks like this:
void EUSCI_A1_ISR(void)
{
int c=-1;
switch(__even_in_range(UCA1IV,USCI_UART_UCTXCPTIFG))
{
case USCI_NONE: break;
case USCI_UART_UCRXIFG:
...
case USCI_UART_UCTXIFG:
// If there's something in the Ring Buffer, transmit it
// If not, then disable TX interrupts until new data gets written.
if (ring_buffer_get(_rbdTx, &c) == 0)
{
EUSCI_A_UART_transmitData(EUSCI_A1_BASE, (uint8_t)c);
}
else
{
EUSCI_A_UART_disableInterrupt(EUSCI_A1_BASE, EUSCI_A_UART_TRANSMIT_INTERRUPT);
// Set TXIFG manually back to 1 for next time.
HWREG16(EUSCI_A1_BASE + OFS_UCAxIFG) |= EUSCI_A_UART_TRANSMIT_INTERRUPT;
}
i am trying to understand a program but i have some questions, maybe you can help me. The microcontroller used is a ATMEL 2549 - 8bit. Thank you in advance.
Atmel-2549 8-bit AVR Microcontroller ATmega640 1280-1281-2560-2561 datasheet
Set up a stop watch with the following features:
• There are three push buttons: START, STOP, and RESET
• Your system generates an interrupt every 100ms by using Timer1.
• There is an LCD at port A. Use the usual library for controlling the
LCD!
• On the LCD, you display the time that has elapsed since the START
button was pushed. Show minutes, seconds and tenth of seconds.
• After 59 min 59.9 s, the display starts from scratch again.
#include <stdint.h>
#include <avr/io.h>
#include "lcd.h"
#include <stdio.h>
#include <avr/interrupt.h>
void timer1_config(void);
void exinterrupt_config(void);
void send_string(void);
display myLCD;
volatile char text[20];
volatile uint8_t minute=0,sekunde=0,zehnt=0;
ISR(TIMER1_COMPA_vect) { //Interrupt for a timer with minute, second and decisecond.
zehnt++; //from 0 to 59min 59,9sec. After that time is elapsed,
if (zehnt>9) { //it should be showed on LCD display.
zehnt=0;
sekunde=sekunde+1;
}
if (sekunde>59) {
sekunde=0;
minute=minute+1;
}
if (minute>59) {
minute=0;
sekunde=0;
zehnt=zehnt+1;
}
send_string();
}
ISR(INT0_vect) { //Interrupt for starting the timer.
// --- No. *1 ---
TCCR1B|= (1<<CS11);
}
ISR(INT1_vect) { //Interrupt for stopping the timer.
TCCR1B&=~((1<<CS10)|(1<<CS11)|(1<<CS12));
}
ISR(INT2_vect) { //Interrupt for resetting the timer.
minute=0; //Sets everything to 0 and shows time on LCD display.
sekunde=0;
zehnt=0;
TCNT1=0;
send_string();
}
int main(void) {
// --- No. *2 ---
DDRD&=~((1<<PIN0)|(1<<PIN1)|(1<<PIN2));
timer1_config(); //Load all three functions.
exinterrupt_config();
send_string();
lcd_init(&myLCD ,&PORTA); //Start the LCD display on port A.
lcd_send_string(&myLCD,"-------Watch-------",1,1);
sei();
for(;;){};
return(0);
}
void timer1_config(void){
TCCR1B|=(1<<WGM12);
OCR1A=12499;
// --- No. *3 ---
TIMSK1|=(1<<OCIE1A);
}
void exinterrupt_config(void){
EIMSK|=((1<<INT0)|(1<<INT1)|(1<<INT2)); //Enable all 3 interrupts.
// --- No. *4 ---
EICRA|=((1<<ISC01)|(1<<ISC11)|(1<<ISC21));
}
void send_string(void){ //Sends text to LCD display.
sprintf(text,"%i.min %i.sek %i.zehnt",minute,sekunde,zehnt);
lcd_send_string(&myLCD,text,3,1);
}
1: I understand this is the command to make the timer start counting, but on the description from the datasheet it says "clkI/O/8 (From prescaler)" for setting the bit CS11 high. I cant understand it and how it works.
2: Is it setting the bits from DDRD to input (0)? If so, why is it being done if port D inst even being used?
3: I dont understand what it does!
4: The description from the datasheet says "The falling edge of INTn generates asynchronously an interrupt request", but i dont really get what it does. Whats the difference to "The rising edge of INTn generates asynchronously an interrupt request"?
Thank you again!
1: Setting CS11 High
From Table 17-6, it really sets the clock to clk(I/O)/8. That means it will increment the internal counter on every eighth tick of the internal I/O clock. Maybe you couldn't count every tick in the timer's register in a second, so you need to prescale it.
2: Setting DDRD bits to input
Those are for the buttons. The buttons must be on PIND of your panel, one bit for each button. Although the program does not read PIND, the external interrupt handler does, so the data direction must be set up accordingly.
Buttons, switches are inputs, leds are outputs. It depend's on your developer panel on which ports are they wired.
3: Setting up TIMSK1
§17.11.36
• Bit 1 – OCIEnA: Timer/Countern, Output Compare A Match Interrupt
Enable When this bit is written to one, and the I-flag in the Status
Register is set (interrupts globally enabled), the Timer/Countern
Output Compare A Match interrupt is enabled. The corresponding
Interrupt Vector (see “Interrupts” on page 101) is executed when the
OCFnA Flag, located in TIFRn, is set.
The timer peripherial can operate in different modes. This setting is related to output compare mode, and it will tell the hardware to issue an interrupt when the timer's internal counter reaches a limit (the limit is set in OCR1A).
The timer is set to CTC (Clear Timer on Compare) mode in TCCR1B (Table 17-2), so it restarts counting when the limit is reached.
4: Falling edge
A falling edge is when a signal goes from high to low. A rising edge is when the signal goes from low to high. These buttons are usually Active-LOW, so a falling edge means the button is pressed. (and a rising edge means the button is released)
(I'll gladly post code if someone can point out how to paste it in here without using the 4 space indentation system that doesn't work)
Hello folks
After ~9Hours racking by brains, I can't find an answer or find where my calculations are going wrong... but they are.
I have a circuit built using a microchip 18F2550 microcontroller.
I am using this circuit to measure the delay between 2 signals and am using the 2 CCP registers in capture mode.
This all works and the result is sent to the PC (over USB serial) all dandy, but the results are wrong.
I have to apply a gain of ~16000 to any results to get somewhere near the delay presented to the pins.
I have the delay set in the line
Timer1 is set as an internal
Timer3 is disabled
relevant interrupts are enabled
and the main routine runs continuously.
When I get a rising edge detection on the CCP1 pin, the interrupt is configured to reset timer1 to zero as well as the overflow counter
#INT_CCP1
void ccp1_isr() // Captures the rising edge of CCP1 pin.
{
if(timing==FALSE){ // only do this on the edge, any bouncing will reset timers etc.
set_timer1(0);
T1_Overflow = 0;
Pulse_Time = 0;
timing = 1; // Set flag to indicate timing.
output_high(BLUE_LED);
}
}
the timing flag ensures the times cannot be reset by another pulse on the CCP1 pin.
Timer1 should then be reset and start counting as normal. Every time it rolls around by 65535 (16bit device) another interrupt is fired after which the amount of overflows are incremented.
#INT_TIMER1
void isr()
{
T1_Overflow++;
}
Finally, when the input pin on CCP2 goes high, the CCP_2 interrupt is triggered. This captures the value of the CCP register (which is the value of Timer0 at the time the interrupt was fired) and the overflow register.
#INT_CCP2
void ccp2_isr()
{
if(timing == TRUE){ // only output this when preceded by CCP1
if(Count_Done == FALSE) // do this once only
{
Count_Done = TRUE; // and also flag to the main routine to output data to the terminal.
Pulse_time = CCP_2;
Pulse_Overflow = T1_Overflow;
measureCount++; // increment the number of measures.
}
output_low(BLUE_LED);
timing = FALSE;
}
}
CCP1 can now start responding to the inputs again.
The idea of this is that every time I get a pulse of one input at CCP1 followed by CCP2, a string is sent to the terminal with a counter, the number of overflows and the time left in the timer.
while(TRUE) // do forever while connected
{
usb_task(); // keep usb alive
if(Count_Done == TRUE)
{
printf(usb_cdc_putc, "%lu , %lu , %lu \r\n",measureCount, pulse_time, pulse_overflow);
Count_Done = FALSE;
}
so, I should get an output to the terminal of something like "1,61553,35" for a ~12ms delay between CCP1 and CCP2.
The problem is that these are the results I am getting for a 200ms pulse provided to the circuit. (Verified twice)
so where am I going wrong.
I have a 48MHZ Clock with no prescaler which implies a cycle every 20ns.
Divide by for 4 instructions per cycle for the clock which implies 5.2ns every cycle
16 bit timer which implies rollover every 65535*5.2ns = 341us per rollover.
when you do the calculations (0.000341*pulse_overflow)+pulse_time*(5.2*(10^-9))
then the above data gives 0012.27ms and not the 200ms provided.
Can anyone point out where I am going wrong with these calculations???
Your error is in "Divide by for 4 instructions per cycle for the clock which implies 5.2ns every cycle"
The counter ticks once every 4 cycles, not 4 times per cycle. So, the correct calculations are:
2.08333E-08 s/cycle of osc
8.33333E-08 s/tick of timer
0.005461333 s/rollover
You are off by a factor of 16.
I have been looking all of for an answer to this problem and have not been able to find anything. When my ISR is triggered, it goes through and does everything that it is supposed to perfectly fine, then before exiting and returning back to the main loop the ISR executes again. Once it has gone though the second time it then returns back to the main loop. This only happens when I use a 115V relay to operate the interrupt.
I am trying to detect when there is a power outage or when the power comes back on. I am using a Pin change interrupt to sense if the relay is closed or open. When the power goes out the relay will open and will trigger the ISR. If I connect this setup to a normal push button or switch everything works as needed and there is no problem, it is only when it is connected to the relay that there is a problem.
Here is the code that I have:(I know I don't need cli I have just been trying everything)
ISR(PCINT2_vect){
cli();
sbi(PORTC,5);
_delay_ms(6000);
cbi(PORTC,5);
for(delay_counter=0;delay_counter<2;delay_counter++)
{
_delay_ms(6000);
}
sbi(PORTC,5);
_delay_ms(6000);
if(bit_is_set(PIND,2))
{
lcd_clrscr();
lcd_puts("Sending SMS");
usart_print("at");
USART_Transmit('\r');
_delay_ms(6000);
for(i=0;i<=1;i++)
{
usart_print("at*smsm2m=");
USART_Transmit('"');
for(j=0;j<11;j++)
{
USART_Transmit(Alert_Numbers[i][j]);
}
usart_print(" Power has been lost");
USART_Transmit('"');
USART_Transmit('\r');
_delay_ms(6000);
}
lcd_clrscr();
lcd_puts("SMS Sent");
_delay_ms(6000);
lcd_clrscr();
lcd_puts("Status:NO POWER");
cbi(PORTC,5);
}
else if(bit_is_clear(PIND,2))
{
lcd_clrscr();
lcd_puts("System Reset");
_delay_ms(6000);
_delay_ms(6000);
usart_print("at");
USART_Transmit('\r');
_delay_ms(6000);
for(i=0;i<=1;i++)
{
usart_print("at*smsm2m=");
USART_Transmit('"');
for(j=0;j<11;j++)
{
USART_Transmit(Alert_Numbers[i][j]);
}
usart_print(" Pump regained power");
USART_Transmit('"');
USART_Transmit('\r');
_delay_ms(6000);
}
lcd_clrscr();
lcd_puts("POWER ON");
_delay_ms(6000);
lcd_clrscr();
lcd_puts("Status: Good");
}
else
{
}
}
int main(void)
{ /*Initializations*/
DDRC = 0x20; // PORTC,5 is now output
sbi(PORTC,5);
USART_Init(51);
lcd_init(LCD_DISP_ON);
lcd_clrscr();
/*Set interrupts*/
DDRD = 0b11111011; // set PD2 to input
PORTD = 0b00000100; // set PD2 to high
PCICR |= (1 << PCIE0);
PCMSK0 |= (1 << PCINT0);
PCICR |= (1<<PCIE2);
PCMSK2 |= (1<<PCINT18);
sei();
lcd_clrscr();
lcd_puts("Status: Good");
/*Main Program Loop: NOP*/
while(1)
{
lcd_clrscr();
lcd_puts("MAIN LOOP");
for(delay_counter=0;delay_counter<3;delay_counter++)
{
_delay_ms(6000);
}
}
}
I am using a Pin change interrupt to sense if the relay is closed or
open.
Don't do that. Seriously. Do not try to hook mechanical switches to an interrupt pin to trigger ISRs.
If you insist on doing so, at least make sure the switch's signal is decently debounced in hardware before it hits the µC's input pin.
Besides, waiting of any kind (_delay_ms(6000);) is not something one wants to have in an ISR.
The reason your code fails: When the signal is bouncing, even if you wait 6 seconds in your interrupt, the flag is set again (while you are still in your ISR). The quick and dirty solution would be to reset the flag just before you exit the ISR.
Two (in my opinion) better solutions:
If you want to do this in software, just set a variable in the ISR routine which indicates a event. Then, in your main loop (or in a function), do the debouncing of the relais (10 to 100ms should be enough).
if you want to do this in hardware, just add a R C combination as a filter to the pin (the c to ground and the r to the relais. Just try some combinations like 100k and 10µF.
My personal recommendation:
do both ;)
Annotations:
- using a 6 second delay in an interrupt routine is really a bad practice. Better: Use a Timer which is called every say 10 ms and increase a counter if the flag is set and the relais indicate power loss. If the relais has power, reset the flag and counter to 0. If the counter reaches 600, this means the power outage is longer than 6 seconds.
Have fun!
I had the problem myself, so I decided to answer it even though it is an old entry:
The reason why the interrupt fires twice is that the interrupt flag is not reset. This is a problem of some atmega types. You can solve the problem by one simple line:
Post this at the very end of you ISR interrupt function:
PCIFR |= (1<<PCIF2);
This will write a "1" into the interrupt-flag bit for the PCINT2 interrupt. If you are using other Interrupts, you have to set other flags to 1. Notice that the interrupt flag is inverted. So a 1 disables the interrupt, whereas a 0 triggers the interrupt.
Have a look into the datasheet:
http://www.atmel.com/images/doc2545.pdf
See Point 13.2.5:
Bit 2 - PCIF2: Pin change interrupt flag 2 -
When a logic change on any PCINT23..16 pin triggers an interrupt request, PCIF2 becomes set
(one). If the I-bit in
SREG and the PCIE2 bit in PCICR are
set (one), the MCU will jump to the
corresponding Interrupt Vector. The flag is cleared
when the interrupt routine is executed. Alternatively, the flag can be cleared by writing a logical one to it.
I hope this helped you and maybe some other people who have the same problem. Just search the datasheet for the name of the register and the bit corresponding to the interrupt flag and set it to 1.
Bye
I want to know more about Arduino Nano timers.
What timers are there?
Do they produce interrupts?
What code would attach an interrupt handler to them?
How is delay() and delayMicroseconds() implemented...
Do they use timer interrupts? (If so, how can I have other code execute during this?)
Or do they repeatedly poll until a timer reaches a certain value?
Or do they increment a value X number of times?
Or do they do it another way?
The best way to think about the Arduino Nano timers is to think about the timers in the underlying chip: the ATmega328. It has three timers:
Timer 0: 8-bit, PWM on chip pins 11 and 12
Timer 1: 16-bit, PWM on chip pins 15 and 16
Timer 2: 8-bit, PWM on chip pins 17 and 5
All of these timers can produce two kinds of interrupts:
The "value matched" interrupt occurs when the timer value, which is added to every tick of the timer reaches a comparison value in the timer register.
The timer overflow interrupt occurs when the timer value reaches its maximum value
Unfortunately, there is no Arduino function to attach interrupts to timers. To use timer interrupts you will need to write slightly more low-level code. Basically, you will need to declare an interrupt routine something like this:
ISR(TIMER1_OVF_vect) {
...
}
This will declare a function to service timer1 overflow interrupt. Then you will need to enable the timer overflow interrupt using the TIMSK1 register. In the above example case this might look like this:
TIMSK1 |= (1<<TOIE1);
or
TIMSK1 |= BV(TOIE1);
This sets the TOIE1 (generate timer1 overflow interrupt, please) flag in the TIMSK1 register. Assuming that your interrupts are enabled, your ISR(TIMER1_OVF_vect) will get called every time timer1 overflows.
The Arduino delay() function looks as follows in the source code (wiring.c):
void delay(unsigned long ms)
{
uint16_t start = (uint16_t)micros();
while (ms > 0) {
if (((uint16_t)micros() - start) >= 1000) {
ms--;
start += 1000;
}
}
}
So internally it uses the micros() function, which indeed relies on the timer0 count. The Arduino framework uses timer0 to count milliseconds, indeed, timer0 count is is where millis() function gets its value.
The delayMicroseconds() function, on the other hand, uses certain well-timed microprocessor operations to create the delay; which function is used depends on the processor and the clock speed; the most common being nop() (no operation) which takes exactly one clock cycle. Arduino Nano uses a 16 MHz clock, and here's what the source code looks like for that:
// For a one-microsecond delay, simply return. The overhead
// of the function call yields a delay of approximately 1 1/8 µs.
if (--us == 0)
return;
// The following loop takes a quarter of a microsecond (4 cycles)
// per iteration, so execute it four times for each microsecond of
// delay requested.
us <<= 2;
// Account for the time taken in the proceeding commands.
us -= 2;
What we learn from this:
1 µs delay does nothing (the function call is the delay)
Longer delays use the left shift operation to time the delay.