Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

RETFIE: Is it required?

Status
Not open for further replies.

jpanhalt

Well-Known Member
Most Helpful Member
Almost blizzard conditions in my area today, so I spent a little time studying interrupts between snowplowing chores.

MCU =12F683
MPASM (Assembly)

I am interested in the External Interrupt and wonder what happens if the interrupt of an MCU (#1) , say from another MCU (#2), returns to an external event you are measuring with MCU#1 such that the measurement is screwed up (e.g., duty cycle).

I know I can turn off the interrupt enable bit during the measurement, but the main reason I don't want to do that is that the measurement is a continuous and slow process (about 20 mS). Results (4 bytes) from each measurement are stored for transmission to MCU#2 . I will be sending the stored data when MCU#2 says it is ready and don't mind losing some data that are in the process of being captured. I just don't want data that are potentially corrupted by the interrupt to be stored.

What happens if you clear the interrupt flag, reset GIE, and then just GOTO some defined place, like START? Since the PC was pushed onto the stack at interrupt but not popped, will I get a stack overflow error?

John
 
To avoid stack overflow, you can clear up the stack and reset it to some pre-defined point. It shouldn't be difficut if you want to wipe out the entire stack.

If you have DMA in the receiving MCU, you probably can program it to receive data from the sending MCU in the background. So when the receiving MCU is ready to process next porton of data it's already received.
 
Not quite sure how you clear up and/or reset the stack in a 683. Can you give an example? Nikolai Golovchenko () has described pushing and popping from the stack using indirect addressing. I haven't studied his examples yet. Here is the POP macro (of course FSR is never reduced to "0" if it works that way): EDIT: Never mind. It is a software stack. Not the real stack

Code:
POP     MACRO
        DECFSZ FSR, F
        ENDM
Right now, the target is a 16F1519, which I don't think has DMA.

The 12F1840 has software reset, which would work, and I have a few of those chips. But everything I have for this project is working on the 12F683 and 16F1519. So, there are alternatives available, but I remain curious about whether it can be done with the 683.

John
 
Last edited:
https://www.microchip.com/forums/m183273.aspx
So long as your startup code makes no assumptions about power-on values of SFRs (i.e. you write to every one that you rely upon), then you can simply do a GOTO 0.
The stack is circular, i.e. it never "overflows", it just overwrites any entries more than 8 deep, so you don't have to worry about it.

I hope that is right. Just ignore and go back to wherever I want to.

John
 
The data sheet for 12F683 says that it has a circular hardware stack which is not accessible from anywhere. It also says that the stack is circular and therefore cannot overflow. Moreover, there's nothing you can do to manipulate stack pointer except for regular CALL, RETURN etc. It is therefore not necessary to do anything to reset the stack. You only need to make sure than when you jump out of the interrupt RETURN is not going to be called. Also, without RETFIE, you need to enable interrupts manually.

There's also a "clean" way to do this. Use a bit flag, which you clear before doing the measurement. Set the flag during the interrupt. Check the flag after the measurement is done. If it is set the measurement might have been corrupted by the iterrupt and you can dismiss the result.
 
It wouldnt do a great deal of good however 8 calls without returns would overwrite any data allready on the stack if thats what you wan to do.

Nice an mild here, no snow.
 
Thank you both for the explanations. If you want a flow chart, I can put one together, but maybe this description will do.

The duty cycle being measured is 100 Hz, so a minimum of 10 mS is required. In the current method, I am polling the signal and using TMR1 (16-bit) to capture it. That could take up to 20 mS, but I can clip that max to about 15 mS without much effort. The rest of the program is just sending 4 bytes of data to MCU#2 (16F1519). Baud will be 9600, so that will take only a little more than 4 mS, and I don't want to interrupt that process. Therefore, using manual setting of the GIE, I was going to steer the external interrupt ("request for data interrupt") to the data measurement cycle. The return, regardless of whether I use RETFIE or GOTO, will check a flag to see if it is from an interrupt (like NorthGuy's suggestion).

1) If I use RETFIE, data will not be saved if that flag is set, but the program may take quite awhile to get back to data collection; but
2) If I return to a location just before the data collection, I will save that time potentially wasted in #1.

In either case, if the flag is set, interrupts will not be enabled for the very next data collection period. That will allow the stored data to be reasonably fresh. Having never done anything like this, I was worried about the stack and am not confident enough in my simulator ability to assume that a problem there would be detected. Based on your understanding of the stack, I will set something up to see how it works.

John
 
You seem to be making life difficult for yourself :D

I would strongly suggest you don't try manipulating the stack and not using RETFIE, there's no need to ever do anything like that, and it would be appalling bad practice to do so.

Does your processor have a hardware USART?
 
No hardware UART and no manipulation of the stack at the 12F683 side. At the 16F1519, yes to both.

At this point, I am just considering the option of returning to a known point in the program on the 12F683 side rather than to an arbitrary location that could waste up to 20 mS.

If a 20-mS wait happens only a few times, no big deal. My over-arching concern is that the display side of the connection will also be slow. I don't know how slow, but it will be doing some 32 by 16 division and writing to a 128X64 GLCD. I am worried that the two "clocks" could get into an adverse beat, like two motors running at different speeds, which could really slow things down. Right now, most of this is just on paper. It is pretty easily done with flags and polling, and that "beat" can be broken with delays. I am just looking at alternatives.

I have gotten one-way communication from the sensor to the display using a pair of XBee's. I would prefer to control the sensor from the display.

John
 
I would do something like this:

I would set up a CCP module as an "Input Capture". In this mode, it captures the value of the timer at every rising (or falling) edge. It then resets the timer and sets up an interrupt. For some reason, it doesn't do both edges. Depending on the nature of the signal, if frequency is constant, measuring either only rising or only falling edges should be enough to calculate the duty cycle.

I would set up an interrupt on a different pin with the receiving MCU.

I would do my regular tasks (or idle if there's nothing else).

When a CCP interrupt comes, I would retrieve the saved value of the timer and store it somewhere. Doing so may delay the interrupt from MCU, but it's a short task, and delaying the interrupt by few us shouldn't be harmful.

When an MCU interrupt comes, I would transfer the list of saved values to the MCU. This takes some time. You say it is 4ms. During this time, interrupts will be disabled. If an interrupt from CCP comes, it'll be pending until you RETFIE. If another interrupt from CCP comes, it'll be lost and you loose the previous timer value. But it is not very likely (if at all possible) because it is probably more than 4ms between consecutive CCP interrupts.
 
The frequency is not sufficiently stable to measure only the low or high times. Microchip has an application note using the same sensor that I am using from Memsic:

https://ww1.microchip.com/downloads/en/AppNotes/00996a.pdf

It is used for pitch and roll measurement. The application note describes only measuring high time (See: page 5). I did that experiment and could not get the precision I need (within 1°), particularly, since my device will be used in a wide range of temperatures. The accelerometer is based on measuring thermal convection and has quite a high variation with temperature. In fact, the frequency change makes a decent thermometer.

I have been plowing and shoveling snow since about 0730 this morning in 10°F weather (had to take a break earlier to let my fingers warm up). We had between 8 and 11 inches plus high winds yesterday. I am beat, but will get to a simulation over the weekend.

John
 
To be frequency-independent, you need to get all the way from rising edge to rising edge. Microchip method depends on 100Hz frequency.

You can do this:

set up a CCP module to produce an interrupt on every leading edge from the sensor. It's likely to be the same or within few counts for both values you're reading (which simplifies things).

The interrupt will remember peak-to-peak value of the timer, so you know the frequency.

In the interrupt, make sure the pin is high, then keep watching the pin until it goes down. Once this happens, record the timer. You can now calculate the duty cycle. While you're doing this, you have two choices:

1. Keep interrupts disabled. The MCU interrupt will be pending until the pins go down and you quit the CCP interrupt, so MCU will not get service right away, but it'll get the most recent information.

or

2. Enable interrupts. The MCU interrupt may come in and then you will loose one measurement.

Alternatively, once you get rising edge interrupt, you can re-program it for falling edge. Then when you get falling edge interrupt, re-program it for rising edge. This will give you both immediacy in serving MCU interrupts and you will not loose any data at all, but it's tricky because they say that re-programming CCP resets the timer, so you will need to save current value of the timer and then somehow account for it.
 
UPDATE

I have attached a flowchart and ASM file. It seems to work as intended in simulation without the RETFIE. Now that that question is answered, I probably won't use it. I will just transmit continuously and let the receiving end decide when to listen.

Note, there is a NOP added before start. For whatever reason, I needed to add that and have the breakpoint there for the simulation to work. Previous versions did not need it. MPLab seem flaky at times. If you try the rough Assembly file (it has not been cleaned), be sure to disable case sensitivity.

Edit: Oh, BTW, I have also attached the stimulus file. It takes abut 5 trips through the program for the interrupt stimulus to fire. That was by design, if it works for you. Forget about the asynchronous stimulus.

John
 

Attachments

  • Wireless Sensor_IRQ.pdf
    26.2 KB · Views: 179
  • Memsic Sensor IRQ.asm
    10.6 KB · Views: 143
  • Sensor Stiumlus v.2_IRQ.zip
    428 bytes · Views: 121
Last edited:
It's pretty simple to use the CCP module to tell you the time high and total time. After a capture occurs, the capture result is read and the rising/falling edge capture mode is toggled. The difference in capture values will be the high time and low times and there is no need to reset the timer; just keep it rolling on the whole time for a more accurate reading (w/out requiring compensation).

The serial sending can be done in an interrupt. The timer2 periodic interrupt can be utilised to send each bit of the serial data.

This approach doesn't have any issue of missing cycles (depending on minimum/maximum duty cycle), multiple interrupts (as there's only a single interrupt) and is more accurate than your polling method.

Is there any reason that you're not using the CCP module on the '1519? The additional '683 seems to be adding complexity without any gain.
 
Thanks, dougy83

I will give the CCP module another look. The input being measured is only 100 Hz. I am unclear how one would know when to toggle modes and get it all done within or or two of the 10-mS cycles. I am assuming you would use the interrupt created by each detection to direct the toggle. Won't reading and storing results from TMR1 at that time cause some lost counts?

Anything more than 100 mS for the loop refresh will give perceptible lag; >200 mS would probably be unacceptable. In my application, the actual period counts are 10,000; the high counts can vary from 4,000 to 6,000 (MCU @ 8 MHz, prescale 1:2). Loop = measure, transmit/receive (XBee), analysis, and display

Additionally, I have thought of measuring 2 to 4 cycles instead of just one to reduce jitter. However, jitter when measuring only one cycle on my test bench has not been too bad.

John
 
Have a look at section 11 of the datasheet (https://ww1.microchip.com/downloads/en/devicedoc/41211d_.pdf) for the CCP module. The LSb of CCP1CON will be 1 for rising edge and 0 for falling edge once you've set everything up. You toggle modes after the module tells you that it's captured an event (i.e. an edge), e.g. by checking the CCP1IF; after toggling the mode, clear the CCP1IF bit. This means you set the mode to "falling edge triggered" if you're expecting a falling edge to be next, and "rising edge triggered" if the next edge will be rising.

There will be no lost count because the CCP module copies the TMR1 count into the CCPR1H:CCPR1L registers, so when you read the value is not so critical. You can use the interrupt to handle the CCP event, which might affect your serial sending (depends on how long the CCP result processing takes and if data is currently being sent), but you can just poll it, which is less hassle and a better bet if the '683 doesn't have anything better to do.

If you send the data using an interrupt serial routine, there's no "lag" or lost readings (except where the '1519 isn't ready to receive).

The jitter you're experiencing may well be due to the way that you're reading the high/low time: I assume you're using a loop to wait for the change of pin state - in this case you'll have a jitter of +/-{loop time} + actual jitter. If you use the CCP module, you'll have +/- 1 cycle + actual jitter.
 
Thanks. Your second paragraph is the piece of information I had missed.

The jitter I am talking about is from physical vibration. When the sensor is put on a solid angle block (>10 kg), the readings are quite stable (S.D. about ± 0.1°).

Mike (K8LH) has posted his TMR2-driven routines, which I will probably use.

John
 
I spent all afternoon reading about CCP capture (I am a very slow reader). As usual, I worried needlessly about what needed to be turned on and off in various SFR's. I finally put something very simple on paper, and simulated it. The numbers come out perfectly. Thank you again.

John

Code:
Data_start
     bsf       T1CON,TMR1ON   ;turn TMR1 on                                     |B0
     movlw     b'00000101'    ;
     movwf     CCP1CON        ;CCP1 rising capture                              |B0
     bcf       PIR1,CCP1IF    ;flag must be cleared after mode                  |B0
     btfss     PIR1,CCP1IF    ;test CCP1 interrupt flag (interrupt not enabled)
     goto      $-1
     movf      CCPR1H,w       ;start time high byte
     movwf     CCP_T1H        ;save value in shadow register
     movf      CCPR1L,w       ;start tiem low byte
     movwf     CCP_T1L        ;save value in shadow register
     bcf       CCP1CON,0      ;change interrupt flag to falling edge
     bcf       PIR1,CCP1IF    ;clear flag

     btfss     PIR1,CCP1IF    ;test CCP1 falling edge interrupt flag
     goto      $-1
     movf      CCPR1H,w       ;stop time high byte
     movwf     CCP_T2H        ;save value in shadow register
     movf      CCPR1L,w       ;stop time low byte
     movwf     CCP_T2L        ;save value in shadow register
     bsf       CCP1CON,0      ;change interrupt flag to rising edge
     bcf       PIR1,CCP1IF    ;clear flag

     btfss     PIR1,CCP1IF    ;test for rising edge (end of period)
     goto      $-1
     movf      CCPR1H,w       ;start time high byte
     movwf     CCP_T3H        ;save value in shadow register
     movf      CCPR1L,w       ;start tiem low byte
     movwf     CCP_T3L        ;save value in shadow register
     bcf       CCP1CON,0      ;change interrupt flag to falling edge
     bcf       PIR1,CCP1IF    ;clear flag
     bcf       T1CON, TMR1ON  ;stop TMR1
 
It looks like you didn't have any trouble with the CCP module.

Note that there's no need to stop and start TMR1 before and after every cycle - if you do this, you'll only be able to read every second cycle (as you'll be partway into the next cycle when you restart TMR1 -- this can be compensated for, but there's no point in compensating every 2nd cycle). Just leave TMR1 running and you can get the captured count after each edge; you can calculate the period of time since the last event by subtracting the last count from the current count. This means no cycles or counts are missed or need compensation.
 
Yes, but if TMR1 overflows during a read, it screws things up, doesn't it? So, you would need to check for that. Probably a very small difference over all.

The calculation came out exact (according to the sim). That was nice to see. There were no lost cycles as happened while I started and stopped TMR1 in the polling method. Now, I have to decide whether to do some of the math at the 683 side and save a few milliseconds of transmission time, or just send the raw data and do it all at the 16F1519 side (both are only at 8 MHz). The subtraction is fairly quick, so I am thinking of doing that with the 683.

John
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top