No announcement yet.

Interrupt context saving

  • Filter
  • Time
  • Show
Clear All
new posts

  • Interrupt context saving

    Sorry to bother you folks with this but I just need a confidence booster. Probably more of a confirmation.

    I've had great success with interrupt application on a PIC16F684A and a PIC 16F628A to read an encoder and other tests.
    Now I am using a PIC16F722A which has more than 2K of ram. to capture button presses on PortB.

    So I am now expecting to have proper interrupt operation after omitting the context saving lines at the very beginning of the handler, correct? {Ref: PBP Manual: 6.5.2 } I was hoping not to have to breadboard a circuit and cook up a test app to convince myself. I understand that I am still including the lines that restore the context.

    In addition I have run across the following lines of code in other interrupt example and am not certain they apply to my hardware:

    ;wsave1 var byte $a0 system ' Necessary for devices with RAM in bank1
    ;wsave2 var byte $120 system ' Necessary for devices with RAM in bank2

    If anyone could comment on where the previous definitions would come into play? Are these used in PICs where shadow ram (mapped to $70) does not exist in higher banks?

  • #2
    An awesome resource for Daryl Taylor's Interrupts (DT_INTS) can be found at He heavily comments what he does and why. There is probably more there than in the PBP manual.
    We can crack this cotton PIC'n thang!


    • #3

      I have had some successful tests using DT_INTS in the past. I don't recall if there was some special benefit in using DT's "tools" as they apply to my design. Instead, I decided to get into straight up interrupt coding which doesn't seem too difficult but probably exposes me to all sorts of subtle issues that DT_INTS might prevent. That said it has been largely a worthwhile learning experience. As usually happens I often breadboard and code a very narrowly focused test app to confirm a very specific behavior. If it works right off I don't learn as much as when it needs debugging, the least of which I learn more about debugging.

      I might look into DT_INTS examples again to see if my current questions can be inferred from that code.

      There is one advantage to being a non-professional coder in that it is never boring. It is also humbling to know that I there is no doubt much more that I don't know than that which I do.


      • #4
        If I had Interrupts that weren't in the least bit time critical, using PBP's "ON INTERRUPT GOTO" was the quick and easy route. However, it polls for Interrupt Flags, and there is quite a bit of latency. If I needed absolute "Right Now" Interrupts, I'd use ASM Interrupts. DT_INTS was that compromise that worked way better than ON INTERRUPT GOTO, but not quite as well as a bare-metal ASM Interrupt. And yes, ASM Interrupt coding has a learning curve.
        We can crack this cotton PIC'n thang!


        • #5

          I appreciate your explaining the relative performance expectations between the 3 methods. Perhaps I could get yout opinion on the method might be most effective for my design. I am still working through the final approach but it have road tested several actions needed in the final control firmware with individual pieces of test code

          Essentially I have 2 dc perm magnet motors and two off-the-shelf motor drivers (each driver board has 1 power pin and 1 direction pin driven by the PIC.) The control box has 4 push buttons, 2 each for CW/CCW control. I intend to limit the control so that both motors do not operate at the same time because they do not have to. There are 4 sensors in the design to detect the end of travel in each direction for each motor. There is no instance then that more than 1 of these sensors will activate at the same time. (Intentional Keep It Simple Stupid philosophy.)

          I have chosen to use the entire PortB for these 8 inputs. Conveniently I can make all PortB pins as inputs and use internal pullups. I make use of "Interrupot-on-portb-change" to take a snapshot of PortB the moment it changes and pass that out back to the main program line for evaluation. I haven't flow-charted the final main line logic yet bit I have tested the buttons and sensors and got expected results in the byte pattern associated with each action.

          My concept for the motor speed control (intended to be constant) is to use a series of pulses on the power pin generated with "Pause" instructions because I don't really want to get into "PWM" The process of looping and pauses has worked well for me in the past.

          So that is a "sketch" of what I see as the design at the present. I have used a similar scheme as this in the past using polling of the inputs without interrupts and it was an acceptable solution. None of this is involving an industrial or scientific process (model railroad automation) so it can all have fairly wide tolerances. No life safety issues!

          If you care to offer an opinion as to whether you think this warrants a "bare-metal ASM Interrupt" as you call it. I still am somewhat in favor of gaining more confidence in using that approach though. The assembly code I have come up with seems to work well and is only 15 operations long excluding the context restore.


          • #6
            Its hard to see why the pic is even needed let alone asm interrupts.

            Click image for larger version

Name:	AB.jpg
Views:	24
Size:	100.9 KB
ID:	8718

            this circuit would easily suffice .unless there is some other undisclosed functionality enshrined.
            i suspect that by trying to control motor speed by using pause's in program execution it is making button and limit switch detection fairly unresponsive and that you are feeling that interrupts are a solution.
            there are better ways. you are using a chip with 2 ccp modules why would you not use them for speed control ?


            • #7

              You're absolutely correct about 1) not needing a PIC and 2) the undisclosed functionality. To simplify the design summary I didn't describe the automation feature. On the control box I am adding an option to run up to 9 cycles of automation (the number of cycles that can be displayed on a single digit LED module.) The motors animate a freight yard crane cab rotation and winch motor on the model railroad placing a load on a freight car.

              No simple manual motor control would likely need a PIC with 28 pins but with the digit display (has HC4511 driver) and various switches needed for options I've been running out of them quickly enough. As far as the CCP modules to pulse the motors, I need only a constant speed once I have established what that is for each motor. I have no experience with CCP modules and very little with PWM so to reduce the chances of getting several additional things wrong (I really lack the nerve to venture into the unknown) I am staying with things that have worked for simpler versions of this project. That said I might do some work in the future with those other PIC subsystems, etc.

              I don't know which aspect of the model railroad construction I find more interesting, the model railroad building or the electronic automation and lighting. This year I completed an upgrade to the trolley line which included automating trips between stations. This included a 2x16 led display, several sensors in the track, motor control for one turnout, etc. I considered it an accomplishment for my novice skills. A few years ago I interfaced a PIC16F747 to control 8 power blocks and 9 turnouts on the freight region of the layout. An 16F747 was interfaced via RS-232 to a laptop running a Visual Studio GUI app that I wrote to use mouse clicks to change the track configurations. Later I modified that PIC-PC interface which replaced it with an RS-232 to USB IC by FTDI and re-coded the GUI for USB. So it's kind of a toss-up how much time I've spent on automation compared to physical modelling.


              • #8
                ok that makes a lot more sense, my approach would be to use a state machine to control the motors.
                this example will control both motors simultaneously and respond to switches within a couple of milliseconds
                i will not start a motor in the direction of an active limit sw , limit switches latch the motor off until its control switch is released
                a motor will stop instantly its control switch is released .
                Click image for larger version

Name:	ab1.jpg
Views:	29
Size:	326.5 KB
ID:	8721

                '* Name : UNTITLED.BAS *
                '* Author : richard *
                '* Notice : Copyright (c) 2022 caveat emptor *
                '* : All Rights Reserved *
                '* Date : 24/07/2022 *
                '* Version : 1.0 *
                '* Notes : *
                '* : *
                ;----[16F722A Hardware Configuration]-------------------------------------------
                cfg1 = _INTRC_OSC_NOCLKOUT
                cfg1&= _WDT_ON
                cfg1&= _PWRT_DIS
                cfg1&= _MCLR_EN
                cfg1&= _CP_OFF
                cfg1&= _BOR_ON
                cfg1&= _BORV_1_9
                cfg1&= _PLL_EN
                cfg1&= _DEBUG_OFF
                __CONFIG _CONFIG1, cfg1
                cfg2 = _VCAP_DIS
                __CONFIG _CONFIG2, cfg2
                define OSC 8
                DEFINE DEBUG_REG PORTA
                DEFINE DEBUG_BIT 0
                DEFINE DEBUG_BAUD 9600
                DEFINE DEBUG_MODE 0
                DEFINE CCP2_REG PORTC 'Channel-2 port
                DEFINE CCP2_BIT 1 'Channel-2 bit
                sw1cw var portb.0 ; motor 1 cw switch
                sw1ccw var portb.1 ; motor 1 ccw
                l1cw var portb.4 ; motor 1 cw limit
                l1ccw var portb.6 ; motor 1 ccw limit
                ena1 var portc.1 ; motor 1 pwm
                da1 var portc.3 ; motor 1 direction pins
                da2 var portc.5 ; motor 1 direction pins
                sw2cw var portb.2 ; motor 2 cw switch
                sw2ccw var portb.3
                l2cw var portb.5
                l2ccw var portb.7
                ena2 var portc.2
                db1 var portc.4 ; motor 2 direction pins
                db2 var portc.6 ; motor 2 direction pins
                led var portc.0 ;debug timing
                m1speed var byte
                m2speed var byte
                switches var byte
                oldswitches var byte
                m1_state var byte ' 0 stopped 1 cw 2 ccw 3 stop
                m2_state var byte ' 0 stopped 1 cw 2 ccw 3 stop
                sw_state var byte[4] ;array of switch states
                sw var byte ;pointer to current switch
                ANSELB = 0
                WPUB = 255
                trisb = 255
                trisc = %10000000
                OPTION_REG.7 = 0 ;individual wpu on
                portc = 0 ;all outputs off
                m1speed = 85 'pw% = speed*100/256 ie 33% = (85 * 100)/256
                m2speed = 170 'pw% = 66%
                HPWM 1, m1speed , 1000 ;motor1 speed
                HPWM 2, m2speed , 1000
                porta.0 = 1
                PAUSE 200
                DEBUG "READY"
                portc = 1 ;led on debug timing
                led = 0 ;debug timing
                gosub check_switches
                led = 1 ;debug timing
                gosub check_motors
                goto main
                select case m1_state
                case 0 ;start motor in chosen direction if limit sw allows
                if sw_state[0] == 2 & l1ccw then
                m1_state = 1 ;record direction
                debug "start1ccw",13
                sw_state[0] = 3
                elseif sw_state[1] == 2 & l1cw then
                m1_state = 2 ;record direction
                debug "start1cw",13
                sw_state[1] = 3
                case 3 ;stop
                m1_state = 0
                debug "stop1",13
                end select
                select case m2_state
                case 0 ;start motor in chosen direction if limit sw allows
                if sw_state[2] == 2 & l2cw then
                m2_state = 1
                debug "start2ccw",13
                sw_state[2] = 3 ;record direction
                elseif sw_state[3] == 2 & l2ccw then
                m2_state = 2 ;record direction
                debug "start2cw",13
                sw_state[3] = 3
                case 3 ;stop
                m2_state = 0
                debug "stop2",13
                end select
                switches = ~portb ;get switches and invert logic
                ;chk limit sw
                IF m1_state THEN ;if motor running
                if m1_state == 2 && switches.4 then m1_state = 3
                if m1_state == 1 && switches.6 then m1_state = 3
                IF m2_state THEN
                if m2_state == 2 && switches.7 then m2_state = 3
                if m2_state == 1 && switches.5 then m2_state = 3
                ' chk sw
                for sw = 0 to 3 ;cycle through all 4 switches
                select case sw_state[sw] ' 0 idle 1 det 2 on 3 actioned
                case 0 ;rudimentary switch debounce
                if oldswitches.0[sw] && switches.0[sw] then sw_state[sw] = 1
                case 1 ;sw activation detected
                if switches.0[sw] then sw_state[sw] = 2
                case 2 ;sw acted upon
                if switches.0[sw] then sw_state[sw] = 3
                case 3 ;sw release
                if !switches.0[sw] then
                sw_state[sw] = 0
                if sw<2 then
                m1_state = 3
                m2_state = 3
                end select
                oldswitches = switches ;save present switch state


                • #9

                  Thanks for offering an interesting solution. I'll study it for useful techniques. Interesting that my previous generation (design) made use of a state machine when there were only 4 buttons to poll and two limit switches. It operated with acceptable latency considering the task.

                  I ventured into interrupts to see if I could improve the latency in sensing the state change by tying all the switches/sensors pulled up to PortB. I also wanted an excuse to hone some skills using interrupts and writing short bits of assembly. I hadn't done much with assembly since a course or two at UW-Milw some 40 years ago.

                  My attempts here may be like swatting a fly with a Howitzer but my main reason for doing this stuff is to make my brain bigger (or denser however that works.) I will study your solution though because it might very well be a more sensible approach in the end.

                  Thanks for all the feedback. I see by all the time you put into this design that it is probably a worthwhile endeavor at least and it calls for honest creativity.


                  • #10
                    I thought as long as I sort of described the design I might as well mention some of the the hardware. The limit switches are (4) DN6848 Hall effect sensors with a Schmitt circuit to drive the TTL input of the PIC directly. The sensors are triggered by small magnets mounted in the model parts. I used the DN6848's mainly because I had a few of them around. I think they may be out of production now though.

                    The motor control boards have a part number of 2960 and since I don't know this forum's policy on specifically naming outside manufacturers I'll just say it comes from a U.S. hobbyist/robotics specialty supplier "out west." The board is equipped to handle PWM input or constant voltage and can implement reversing DC. The board is for brushed-type motors specifically. In the past I was using H-bridge IC's with added diodes as circuits burned on my boards but now I use manufactured motor drivers which handle all the diode spike protection, etc. in a very small footprint. It is such a luxury to connect the TTL inputs of these pre-made modules right to the PIC.


                    • #11
                      those pololu BD65496 2960 motor drivers are not a good match to your pic chip if you want to use the pic's ccp-pwm module for bi directional speed control for two motors with different speeds.
                      something like a 16f1847 with steerable pwm or a chip with pps makes life easier. The old "L293/8" clunkers are easier to use with less advanced pic chips for bi directional speed control if they can supply enough current for your project

                      there should be no issue with posting links to whatever modules you are using in a pbp project


                      • #12

                        In reply to your previous post, it is probably a good thing then that I plan to just pulse the motor driver power pin using a brute force method like timed high and low states on the pin. It is likely a good thing then that the 2960 has a low current drain on the control pin connected to the PIC and handles the motor power through a MOSFET I think (I don't remember all the details of the board output side at the moment.) At any one time the PIC should not have to source more than it's max current capability because it is rarely ever sourcing more than one output. I tested my proposed setup with a PIC using the "brute force" high/pause/low/pause method and got very good results.

                        My approach to overall design and coding is to employ lots of small test setups that confirm/refine the various operations that are required to make up the entire control scheme. Then I put them all together in the complete main program flow with the "modules" often as subroutines. The benefit to me is that I get to learn about a variety of things and don't get bogged down trying to tackle the business all at once. That method has been working quite well and I get a bit more confident in my abilities as I go.


                        • #13
                          it is probably a good thing
                          not really, using the ccp modules is always a better solution. i did not say it cannot be done using a "2960" module it just that it needs two extra resistors per motor to aid in pwm signal steering

                          the load i was referring to is the motor current not the pic interface


                          • #14

                            Just to clarify, with any ccp module, is it possible to generate two different pulse specifications on each different channel? I suppose the answer to that is yes because it would be a pretty limited capability for the PIC if it were not so. My other concern in this project is with so much stuff going on I have always wondered if there might be some some potential for conflicting hardware interactions that would create a knotty problem to debug.

                            I see on my PIC ccp can be implemented on PortC pins so I could remap the functions I have now assigned to them. I see ccp2 could be mapped to PortB but I really want to keep all 8 of them as inputs. I would have to run some test setups to confirm a working system since I have no prior ccp experience.

                            Well, there is a lot to do on this project and I am just beginning to test sub-system setups.

                            Thanks for your ideas.


                            • #15
                              See if your PIC has an Alternative Pin Function. Older versions control this either in the CONFIGs or an APFCON Register. This may allow you to shuffle something. Of course, newer PICs have Peripheral Pin Select (PPS) that makes it a whole lot easier.
                              We can crack this cotton PIC'n thang!