View previous topic :: View next topic |
Author |
Message |
DDLP
Joined: 08 Jun 2011 Posts: 7
|
Posted: Wed Jun 08, 2011 3:45 pm Post subject: Interrupts, Help! |
|
|
Hey there, I'm new to these forums and using BASCOM/AVR products and whatnot, so I would really appreciate some help! So this might be kind of a tricky problem but basically I'm using an ATmega324p chip, and I'm trying to run a stepper motor constantly, and use an interrupt triggered by a pushbutton to turn on a LED and then update an LCD display to show which LED is on. My problem is with the pushbutton and therefore the LED/LCD setup, basically I can't get a consistent trigger for whatever reason, it's not very responsive and seems to turn the LED on haphazardly. Here's my code so far, if someone wouldn't mind taking a look and perhaps giving me some ideas/suggestions. Thanks!
Code: |
$regfile = "m324pdef.dat"
Ddrc = &B1111_1100
Ddrd = &B0111_1011
Deflcdchar 0 , 238 , 255 , 255 , 255 , 238 , 238 , 238 , 224
Deflcdchar 1 , 238 , 241 , 241 , 241 , 234 , 234 , 238 , 224
Config Debounce = 20
'--------Declare-Variables-----------------------------------------------------'
Dim Count As Byte
Dim Count1 As Byte
'--------Declare-Constants-----------------------------------------------------'
'--------Declare-Aliases-------------------------------------------------------'
Switch2 Alias Portc.6
Switch3 Alias Portc.7
Fullstep Alias Portd.5
Resetmotor Alias Portd.4
Outputenable Alias Portd.3
Motordirection Alias Portd.6
Clockmotor Alias Portd.1
Motorphase Alias Portd.0
'--------Declare-Interrupts----------------------------------------------------'
Enable Interrupts
Config Int0 = Rising
'--------Declare-Subroutines---------------------------------------------------'
Declare Sub Pulseout1
Declare Sub Light1
'********************************MAIN*PROGRAM**********************************'
'******************************************************************************'
Disable Int0
Cursor Off
Cls
Lcd "L1: L2: L3: "
Locate 1 , 4
Lcd Chr(0)
Locate 1 , 9
Lcd Chr(0)
Locate 1 , 14
Lcd Chr(0)
Lowerline
Lcd "L4: L5: L6: "
Locate 2 , 4
Lcd Chr(0)
Locate 2 , 9
Lcd Chr(0)
Locate 2 , 14
Lcd Chr(0)
Outputenable = 1 'Motor On
Motorphase = 1 '2 phase
Fullstep = 0 'fullstep mode
Resetmotor = 1 'Run Normally
Clockmotor = 0
Enable Int0
Do
Outputenable = 0
Clockmotor = 0
Waitus 200
Clockmotor = 1
Waitus 200
Loop
End
'******************************************************************************'
'******************************************************************************'
'--------Interrupt-Handlers----------------------------------------------------'
On Int0 Chooselight
Chooselight:
Debounce Pind.7 , 1 , Light1 , Sub
Return
'------------------------------------------------------------------------------'
'--------Subroutines-----------------------------------------------------------'
Sub Light1
If Count = 0 Then
Set Switch3
Locate 1 , 4
Lcd Chr(1)
Count = 1
Elseif Count = 1 Then
Reset Switch3
Locate 1 , 4
Lcd Chr(0)
Count = 0
End If
End Sub |
|
|
Back to top |
|
|
DDLP
Joined: 08 Jun 2011 Posts: 7
|
Posted: Wed Jun 08, 2011 5:44 pm Post subject: |
|
|
"Interrupt Flags can also be cleared by writing a logic one to the flag bit position(s) to be cleared." So a better question might be how do I write a logic one to the flag bit position, or what's the syntax really. Thanks. |
|
Back to top |
|
|
DDLP
Joined: 08 Jun 2011 Posts: 7
|
Posted: Wed Jun 08, 2011 7:55 pm Post subject: |
|
|
So just in case any other newbs are having similar troubles... To solve this issue I changed the INT0 trigger to falling, and used a bitwait low so stop any bouncing after the button was pushed, and then cleared the external interrupt flag register to stop any bouncing that may have occurred when the interrupt was triggered....or I just got lucky, anyways here's the changed bit of code.
Code: | On Int0 Chooselight
Chooselight:
Disable Int0
Bitwait Pind.7 , Reset
Gosub Light1
Waitus 1000
Eifr.intf0 = 1
Enable Int0
Return
Return
|
|
|
Back to top |
|
|
mattcro
Joined: 03 Oct 2007 Posts: 327 Location: Scotland
|
Posted: Wed Jun 08, 2011 8:02 pm Post subject: |
|
|
To clear the interrupt flag (best to do it just before returning from the interrupt handler), just use something like:
It might be better to use a timer interrupt to clock the stepper motor. That way you can set up a constant step rate, and the main loop can handle the button and LCD updates. It's also bad practice to have delays and things like bitwait inside an interrupt handler. It doesn't matter so much for the program so far, but if you add other interrupts, serial comms etc you will have problems because the interrupt handler delays will stop anything else happening.
EDIT: just saw you update post. You don't need to disable and enable the interrupt inside the handler, because interrupts are automatically disabled when you enter an interrupt handler, and enabled when you exit. _________________ If all else fails, read the manual. Even better: read the manual before something fails. If you can't find it in the manual, search the forum.
BascomAVR 2.0.8.5 |
|
Back to top |
|
|
AdrianJ
Joined: 16 Jan 2006 Posts: 2483 Location: Queensland
|
Posted: Wed Jun 08, 2011 11:56 pm Post subject: |
|
|
There is already a Bascom solution for reading switches. Look up DEBOUNCE in the Bascom Help.
Mattcro is right, you should not use wait statements inside interrupt routines.
IMHO, you should not use an interrupt at all to read a mechanical switch. IRQs are for reading fast things, down in the order of microseconds, where the processor cannot easily access the line that fast. Mechanical switches pushed by humans are at most 5 times a second. There you can use a poll in the main loop, or even better use a timer ISR to poll the switch at a fixed rate. 100 times a second is plenty fast enough. _________________ Adrian Jansen
Computer language is a framework for creativity |
|
Back to top |
|
|
ivangel
Joined: 20 Sep 2010 Posts: 41 Location: Poland
|
Posted: Thu Jun 09, 2011 11:25 am Post subject: |
|
|
You are right but the interrupt is one great advantage: it generates an interrupt, and interrupts the main program which is sometimes necessary ... |
|
Back to top |
|
|
DDLP
Joined: 08 Jun 2011 Posts: 7
|
Posted: Thu Jun 09, 2011 2:45 pm Post subject: |
|
|
Hey thanks for the suggestion guys. @mattcro I'll have to try that timer interrupt for the motor. I definitely only disabled and enabled the interrupt as a precaution but I gather bascom is unique in that you don't have to, so I'll get rid of that. I only used the bitwait so that if on pushing the button, it bounced into the interrupt, it would have to wait for the button to be released to switch on the led, but that's definitely "kludgy". I have read that the external interrupts are commonly used for pushbuttons, and in this case I used it because I actually need the motor to turn back and forth like an oscillating fan, so I don't think I could really poll the main loop for satisfied IF statements, I'll try the timer ISR though that sounds great. @ AdrianJ I have used the debounce in other programs(and I tried in this one if you look at the first bit of code), but I had no success with it in this program although I don't think I was clearing the interrupt flag when trying it. It was very finicky in any case, perhaps it's just an even worse idea within an interrupt.
Thanks again all,
DDLP
EDIT: I was just thinking I probably could use if statements if I used a simple counter to count the number of times the motor pulse has been sent, and then at a certain value switch directions...oh well, my goal was to learn about interrupts, and I guess I've achieved that at least haha. |
|
Back to top |
|
|
AdrianJ
Joined: 16 Jan 2006 Posts: 2483 Location: Queensland
|
Posted: Thu Jun 09, 2011 11:46 pm Post subject: |
|
|
You definitely should not use DEBOUNCE inside an interrupt. For a start, it looks for both edges of a switch closure, whereas the IRQ looks only for one edge ( usually ). So it will never work properly.
If you construct your main loop so it normally executes in a few msec, then polling a switch, or using DEBOUNCE, will work perfectly. That is not to say that you cant run much longer routines in a main loop. but you can usually organize it to run those only when some condition is met, so not on every main loop cycle.
Refreshing an LCD is typical. You cant read an LCD faster than about 2 per second, so why refresh it any faster ? So you can run a fast loop, and only refresh the LCD every nth loop, n being a counter set so that the refresh time is about 500 msec. Many other processes can be handled the same way. _________________ Adrian Jansen
Computer language is a framework for creativity |
|
Back to top |
|
|
DDLP
Joined: 08 Jun 2011 Posts: 7
|
Posted: Tue Jun 14, 2011 4:32 pm Post subject: |
|
|
Hey, so just as a follow up to this thread, I did end up using TIMER0 to run the motor, as it runs it very smoothly, as opposed to running it from a main loop and subroutine which sends the pulses out much slower, resulting in choppy movement. I also opted to use a dip switch for the LED's, and given that I used the timer interrupt for the motor, it is seemingly necessary to use interrupts(both external and pc) to trigger the LED's, in any case it runs very smoothly using all interrupts. Here's the latest iteration of the code, but please note it's not a final pristine product. Also please note that the LCD is configured using the built-in LCD options.(Options->Compiler->LCD)
Code: |
'-----------------------------------------------------------------------------------------
'name : motor.timer.test4.bas
'author : DDLP
'purpose : stepper motor + LEDs + LCD
'-----------------------------------------------------------------------------------------
$regfile = "m324pdef.dat"
Ddrc = &B1111_1100
Ddrd = &B0111_0011
Config Porta.7 = Input
Deflcdchar 0 , 238 , 255 , 255 , 255 , 238 , 238 , 238 , 224
Deflcdchar 1 , 238 , 241 , 241 , 241 , 234 , 234 , 238 , 224
'--------Declare-Variables-----------------------------------------------------'
Dim Count As Byte
Dim Count1 As Word
'--------Declare-Constants-----------------------------------------------------'
'--------Declare-Aliases-------------------------------------------------------'
Switch1 Alias Portc.5
Switch2 Alias Portc.6
Switch3 Alias Portc.7
Switch4 Alias Portc.4
Fullstep Alias Portd.5
Resetmotor Alias Portd.4
Outputenable Alias Portc.2
Motordirection Alias Portd.6
Clockmotor Alias Portd.1
Motorphase Alias Portd.0
'--------Declare-Interrupts----------------------------------------------------'
Enable Interrupts
Enable Timer0
Config Timer0 = Timer , Prescale = 1
On Timer0 Pulseout1
Enable Int0
Enable Int1
Enable Pcint2
Enable Pcint0
Config Int0 = Change
Config Int1 = Change
On Int0 Light2
On Int1 Light1
On Pcint2 Light3
Pcmsk2 = &B00000010
On Pcint0 Light4
Pcmsk0 = &B10000000
'--------Declare-Subroutines---------------------------------------------------'
Declare Sub Pulseout1
Declare Sub Light1
Declare Sub Light2
Declare Sub Light3
Declare Sub Light4
'********************************MAIN*PROGRAM**********************************'
'******************************************************************************'
Disable Interrupts
Cursor Off
Cls
Lcd "L1: L2: L3: "
Locate 1 , 4
Lcd Chr(0)
Locate 1 , 9
Lcd Chr(0)
Locate 1 , 14
Lcd Chr(0)
Lowerline
Lcd "L4: L5: L6: "
Locate 2 , 4
Lcd Chr(0)
Locate 2 , 9
Lcd Chr(0)
Locate 2 , 14
Lcd Chr(0)
Motorphase = 1 '2 phase
Fullstep = 0 'fullstep mode
Resetmotor = 1 'Run Normally
Clockmotor = 0
Enable Interrupts
Do
Loop
'******************************************************************************'
'******************************************************************************'
'--------Interrupt-Handlers----------------------------------------------------'
'--------Subroutines-----------------------------------------------------------'
Pulseout1:
If Count1 <= 250 Then
Outputenable = 0
Motordirection = 0
Clockmotor = 0
Waitus 400
Clockmotor = 1
Waitus 400
Count1 = Count1 + 1
Elseif Count1 < 500 Then
Outputenable = 0
Motordirection = 1
Clockmotor = 0
Waitus 400
Clockmotor = 1
Waitus 400
Count1 = Count1 + 1
Elseif Count1 >= 500 Then
Count1 = 0
End If
Return
Light1:
If Pind.7 = 1 Then
Set Switch3
Locate 1 , 4
Lcd Chr(1)
Elseif Pind.7 = 0 Then
Reset Switch3
Locate 1 , 4
Lcd Chr(0)
End If
Return
Light2:
If Pinc.0 = 1 Then
Set Switch2
Locate 1 , 9
Lcd Chr(1)
Elseif Pinc.0 = 0 Then
Reset Switch2
Locate 1 , 9
Lcd Chr(0)
End If
Return
Light3:
If Pinc.1 = 1 Then
Set Switch1
Locate 1 , 14
Lcd Chr(1)
Elseif Pinc.1 = 0 Then
Reset Switch1
Locate 1 , 14
Lcd Chr(0)
End If
Return
Light4:
If Pina.7 = 1 Then
Set Switch4
Locate 2 , 4
Lcd Chr(1)
Elseif Pina.7 = 0 Then
Reset Switch4
Locate 2 , 4
Lcd Chr(0)
End If
Return
|
Cheers,
DDLP |
|
Back to top |
|
|
AdrianJ
Joined: 16 Jan 2006 Posts: 2483 Location: Queensland
|
Posted: Wed Jun 15, 2011 12:23 am Post subject: |
|
|
Good that your code works, and you are happy with it.
I would have done it more like this:
Code: |
'-----------------------------------------------------------------------------------------
'name : motor.timer.test4.bas
'author : DDLP, modified by AJ
'purpose : stepper motor + LEDs + LCD
'-----------------------------------------------------------------------------------------
$regfile = "m324pdef.dat"
Ddrc = &B1111_1100
Ddrd = &B0111_0011
Config Porta.7 = Input
Deflcdchar 0 , 238 , 255 , 255 , 255 , 238 , 238 , 238 , 224
Deflcdchar 1 , 238 , 241 , 241 , 241 , 234 , 234 , 238 , 224
'--------Declare-Variables-----------------------------------------------------'
Dim Count As Byte
Dim Count1 As Word
dim bIRQFlag as byte
'--------Declare-Constants-----------------------------------------------------'
'--------Declare-Aliases-------------------------------------------------------'
Switch1 Alias Portc.5
Switch2 Alias Portc.6
Switch3 Alias Portc.7
Switch4 Alias Portc.4
Fullstep Alias Portd.5
Resetmotor Alias Portd.4
Outputenable Alias Portc.2
Motordirection Alias Portd.6
Clockmotor Alias Portd.1
Motorphase Alias Portd.0
'--------Declare-Interrupts----------------------------------------------------'
' Enable Interrupts dont enable IRQs here !
'this may cause IRQs to be serviced while setting them up
Enable Timer0
Config Timer0 = Timer , Prescale = 1
On Timer0 Pulseout1
Enable Int0
Enable Int1
Enable Pcint2
Enable Pcint0
Config Int0 = Change
Config Int1 = Change
On Int0 Light2
On Int1 Light1
On Pcint2 Light3
Pcmsk2 = &B00000010
On Pcint0 Light4
Pcmsk0 = &B10000000
'--------Declare-Subroutines---------------------------------------------------'
'you do not have to DECLARE subroutines when they are actually ISRs
'in fact doing so might cause the compiler to insert RET opcodes
'rather than RETI opcodes, which will cause serious problems.
Declare Sub Pulseout1
Declare Sub Light1
Declare Sub Light2
Declare Sub Light3
Declare Sub Light4
'********************************MAIN*PROGRAM**********************************'
'******************************************************************************'
' Disable Interrupts not necessary if not enabled above
Cursor Off
Cls
Lcd "L1: L2: L3: "
Locate 1 , 4
Lcd Chr(0)
Locate 1 , 9
Lcd Chr(0)
Locate 1 , 14
Lcd Chr(0)
Lowerline
Lcd "L4: L5: L6: "
Locate 2 , 4
Lcd Chr(0)
Locate 2 , 9
Lcd Chr(0)
Locate 2 , 14
Lcd Chr(0)
Motorphase = 1 '2 phase
Fullstep = 0 'fullstep mode
Resetmotor = 1 'Run Normally
Clockmotor = 0
Enable Interrupts
Do
if bIRQFlag.0 = 1 then 'handle flag set by timer
reset bIRQFlag.0
If Count1 <= 250 Then
Outputenable = 0
Motordirection = 0
Clockmotor = 0
Waitus 400
Clockmotor = 1
Waitus 400
Count1 = Count1 + 1
Elseif Count1 < 500 Then
Outputenable = 0
Motordirection = 1
Clockmotor = 0
Waitus 400
Clockmotor = 1
Waitus 400
Count1 = Count1 + 1
Elseif Count1 >= 500 Then
Count1 = 0
End If
end if
if bIRQFlag.1 = 1 then 'handle flags set by switches
reset bIRQFlag.1
If Pind.7 = 1 Then
Set Switch3
Locate 1 , 4
Lcd Chr(1)
Elseif Pind.7 = 0 Then
Reset Switch3
Locate 1 , 4
Lcd Chr(0)
End If
end if
if bIRQFlag.2 = 1 then
reset bIRQFlag.2
If Pinc.0 = 1 Then
Set Switch2
Locate 1 , 9
Lcd Chr(1)
Elseif Pinc.0 = 0 Then
Reset Switch2
Locate 1 , 9
Lcd Chr(0)
End If
end if
if bIRQFlag.3 = 1 then
reset bIRQFlag.3
If Pinc.1 = 1 Then
Set Switch1
Locate 1 , 14
Lcd Chr(1)
Elseif Pinc.1 = 0 Then
Reset Switch1
Locate 1 , 14
Lcd Chr(0)
End If
end if
if bIRQFlag.4 = 1 then
reset bIRQFlag.4
If Pina.7 = 1 Then
Set Switch4
Locate 2 , 4
Lcd Chr(1)
Elseif Pina.7 = 0 Then
Reset Switch4
Locate 2 , 4
Lcd Chr(0)
End If
end if
Loop
'******************************************************************************'
'******************************************************************************'
'--------Interrupt-Handlers----------------------------------------------------'
'--------Subroutines-----------------------------------------------------------'
Pulseout1:
set bIRQFlag.0
Return
Light1:
set bIRQFlag.1
Return
Light2:
set bIRQFlag.2
Return
Light3:
set bIRQFlag.3
Return
Light4:
set bIRQFlag.4
Return
|
_________________ Adrian Jansen
Computer language is a framework for creativity |
|
Back to top |
|
|
DDLP
Joined: 08 Jun 2011 Posts: 7
|
Posted: Wed Jun 15, 2011 1:36 am Post subject: |
|
|
Great, thanks for taking the time to put that together, I'll have to try it out tomorrow. |
|
Back to top |
|
|
DDLP
Joined: 08 Jun 2011 Posts: 7
|
Posted: Thu Jun 16, 2011 2:27 pm Post subject: |
|
|
Hey so I tried out you're program Adrian, but it doesn't seem to work at all. For whatever reason the motor wouldn't spin, and the lights wouldn't trigger. I believe it might be because if the timer interrupt is executing so quickly, or even before the pulseout is finished and then just piling up, then the main loop is never going to run, and it would essentially just keep pointlessly setting birqflag.0 without doing anything else. |
|
Back to top |
|
|
AdrianJ
Joined: 16 Jan 2006 Posts: 2483 Location: Queensland
|
Posted: Thu Jun 16, 2011 11:23 pm Post subject: |
|
|
Hmm. Yes I suppose its possible. Timer0 is an 8 bit timer, so max count 256. And since you use prescale=1, that is 256 processor clock cycles. It might take nearly that to enter and exit the timer IRQ. You dont give the processor clock frequency, so I suppose its the default 1 MHz. You should really tell the compiler what frequency you run the processor at with the $crystal directive near the start of your code, else the value is taken from the compiler defaults, which may not be what you actually use in the processor.
Do you need the IRQ that often ? If you have those waitus 400 statements in your IRQ, then in fact the timer IRQ is not being executed every 256 usec either, its just that you dont notice it when you run that way. Typical steppers I have seen use pulses around every 1 msec or so at max, which is roughly what you have when you put the waits in the timer IRQ. You would get the same effect by setting a timer0 prescale of say 8 or even 64.
If you want finer control over the motor pulse timing, consider using Timer1, which has a 16 bit range. Then you reload the timer value inside the IRQ with a value to set the pulse width to whatever you need, in the range 0 - 65535. In reality the bottom limit is about 200, not 0. And remember the timer counts from the value you set up to 65535, then the IRQ happens as it rolls over to 0 again.
Of course, you could simply revert to your code, which works. The problem with that is that it is not really working how you think it is, so changing anything in it is likely to result in unexpected behaviour, and tracing exactly what is wrong in an even more complex program can be a hard task. _________________ Adrian Jansen
Computer language is a framework for creativity |
|
Back to top |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum You cannot attach files in this forum You cannot download files in this forum
|
|