RBIE interrupt works only once - PIC16F877A - c

In my code, I have two interruptions, one is coming from the overflow of the TMR0, and the other one is when a button is pressed.
this is the code in MikroC :
int compt = 0;
int seconds = 10 ;
int enable = 0;
void interrupt(){
if (INTCON.INTF) {
PORTD = 9;
enable = 1;
seconds = 10;
INTCON.INTF = 0;
}
if (INTCON.TMR0IF) {
compt++;
INTCON.TMR0IF = 0;
TMR0 = 0x06;
}
}
void main() {
TRISB = 0x01;
PORTB = 0;
PORTD = 0;
TRISD = 0x00;
INTCON = 0xB0;
OPTION_REG = 0x44;
TMR0 = 0x06;
while(1){
if (compt == 625){
if (enable) seconds--;
compt = 0;
}
if (seconds > 0 && enable == 1) {
PORTD = seconds;
PORTB.RB1 = 1;
} else {
enable = 0;
PORTB.RB1 = 0;
PORTD = 0;
}
}
}
what I am trying to achieve with my code is as shown in the following picture :
When I press one of the push buttons, the countdown starts and the LED illuminates until the countdown ends, and if the user pressed the button while the countdown still didn't hit 0, it starts over, until the countdown hits 0 again, then the LED should turn off.
What I'm facing here, is that the interruption from RBIE works only once, the second time I press the button, nothing happens.
I am not sure if the TMR0F has something to do with that or not, tried many things, but couldn't make it to work.
I Hope that you could see something i didn't notice, and help me.

The code as posted compiles without warnings or errors with MikroC.
The code runs using the simulator in MLPAB v8.92 and when using the simulator stimulus to assert the INT0 interrupt it is handled correctly each time.
Your circuit diagram looks like it was created using Proteus, perhaps there are issues with how that simulator works.
The only suspicious setting I can find is that the weak pull-ups are enabled for PORTB yet your circuit diagram has a 10K ohm pull-down in the INT0(RB0) pin.
I would suggest setting bit 8 of the OPTION_REG to one to turn off the PORTB pull-ups.
Sorry my answer is not more definite but I cannot reproduce your problem from the information posted.
Seem like this question was asked on StackExchange too.

You have enabled internal weak pull up resistor and also connected pull down resistor on pin RB0, external resistor is not needed, also you need to provide some amount of delay (about 300ms) after the button is pressed.

Related

Problem reading rotary encoder with atmega8

I am trying to read a rotary encoder as part of a larger project. I am using a Atmega8L and MPLab X with the XC8 compiler.
The issue I am having, is that the encoder does not work reliable and only seems to be able to count up and not down.
Due to some other aspects of the circuit the encoder pins could not be connected to interrupt-capable pins of the atmega8.
Here is my code, that reads the encoder.
#include <xc.h>
#include "pinmapping.h"
#include "main.h"
static inputEvent lastEvent;
static int aLastState;
static int aState;
//get the last Events data for use elsewhere
inputEvent getLastInputEvent(){
return lastEvent;
}
void initEncoder(){
//get our current button state and set rotation change to 0
if((PINB & ENCODER_SW) == 0){
//also save that to our Event
lastEvent.buttonPressed = 1;
}
else{
lastEvent.buttonPressed = 0;
}
lastEvent.rotationChange = 0;
//also save the current state of the A pin for the first read
aLastState = (PINB & ENCODER_A);
if(aLastState >= 1){
aLastState = 1;
}
}
//this should run regularly eg. on a timer interrupt
void readEncoder(){
//first get the state of both pins
aState = (PINB & ENCODER_A);
int bState = (PINB & ENCODER_B);
if(aState >= 1){
aState = 1;
}
if(bState >= 1){
bState = 1;
}
//check if the button is pressed
if((PINB & ENCODER_SW) == 0){
//save that to our Event
lastEvent.buttonPressed = 1;
}
else{
lastEvent.buttonPressed = 0;
}
//check if the state has changed since last time
if(aState != aLastState){
//check if A and B have the same state -> positive rotation
if(bState != aState){
//save the rotation change to our Event
lastEvent.rotationChange = 1;
}
else{
lastEvent.rotationChange = -1;
}
//save the current state of the A pin for next time
aLastState = aState;
}
else{
//if no rotation change happend, save that to our Event
lastEvent.rotationChange = 0;
}
}
The button press is working properly, but the rotation change is never -1 and always 1 or 0.
The ENCODER_SW, ENCODER_A and ENCODER_B are bitmasks, that are all 0s and a single 1 at the bit corresponding to the correct IO-pin.
In the main, first the initEncoder is called (after IO-setup) and then in the infinite loop a function is called, which in turn calls readEncoder() and then getLastInputEvent() and then does some more stuff before returning to the main.
I have also tried running the readEncoder() function on a timer interrupt roughly every ms. This however results in it not working at all.
What is the problem? Why isn't it working?
Edit: Updated the code after I made some changes.
I am using a ALPS EC12E with pushbutton.
The rotationChange is used to increment/decrement a number that is printed to an LCD.
Edit 2: I now think, it is a timing problem, since I tried running basically the same code, just without anything else on an Arduino and printed the values to the serial console and it worked basically perfectly.
I checked what else is done and realized that in my current code the readEncoder() function only runs about once every 98 or so ms.
When I run the function on a timer interrupt every 1 or 2 ms, it just doesn't work at all. I don't even get a reaction to the button push anymore, which is something I do not understand at all.
In fact the timer does not seem to run at all, or at least the interrupt service routine is never executed. (I tried turning on an LED in the setup and turning it off in the isr, but it stays on indefinitely.)
So I guess theres a new problem now...
Here is the timer initialization:
ASSR = 0x00; //make sure we are not in asynchronous mode
OCR2 = 3; //Output compare register
TCNT2 = 0; //Reset counter to 0
TCCR2 = 0b00001111; //Timer 2 CTC mode, clkio/1024
This is done inside of initDecoder(). The OCIE2 bit is already set earlier in the main setup, enabling the timer 2 interrupt.
The isr than just looks like this:
void __interrupt (TIMER2_COMP_vect_num) timer2_isr(){
//Just read the encoder
readEncoder();
}
The interrupt flag gets cleared by hardware when the isr is executed.
The TIMER2_COMP_vect_num is definitely correct as well, as MPLab recognizes it and I can even look at its definition.

8051 two timers and Interrupts problem in c

I want to make two timer works at the same time but it seems not working at all
I wrote the code to blink led.
The led would blink when i used either one of the timer and interrupt
when I used them both, two ports for led are not working.
Was there any rule that can't use two interrupts or timers at the same time?
or just my mcu broken?
btw I am using AT89S52
coding by keil uVision5
and program with WLpro
Here is my code
#include <reg52.h>
sbit LED = P0 ^ 5;
sbit LED2 = P0 ^ 6;
int i = 0;
int y = 0;
int x = 0;
int count = 0;
void blink2()
interrupt 3
{
TH1=0x7d;
TL1=0xec;
y++;
if(y==100) {
if(i==1) {
LED=0;
x=0;
}
else {
LED=1;
x=1;
}
y=0;
}
}
void blink1()
interrupt 1
{
TH0=0xd8;
TL0=0xf0;
count++;
if(count==100) {
if(i==1) {
LED2=0;
i=0;
}
else {
LED2=1;
i=1;
}
count=0;
}
}
void main() {
TMOD = 0x11; // timer mode
TH0 = 0xd8;
TL0 = 0xf0;
TH1 = 0x7d;
TL1 = 0xec;
TR0 = 1;
TR1 = 1;
IE = 0x8a;
}
In blink2() you test the global variable i, but you never change it.
Note aside: blink1() manages LED2, while blink2() manages LED. Perhaps a more consistent naming would help, and the same applies to the variables i, x, y and count.
There is no restriction to use more than one interrupt concurrently, but when of them executes, the others are temporarily blocked. It is not your problem here, your code is fine; but if you want an interrupt be able to be interrupted in turn, for low latency, you must re-enable interrupts in the (relatively) "slow" handler.
Last suggestion: where you have "if (y==100)" and lately "y=0", if you move "y=0" just below "if (y==100)" readability improves.
All the rest seems ok, but I would double-check the setup of the timers; I don't have at hand the datasheet, may be there is a flag to clear in the interrupt handler (I can't remember). Given that you say that a single timer works, I suppose you know what to do, two timers should run without any problem.

Button activated UART message

"I'm trying to use the button that when it is pressed, it triggers to send a string message via UART, but upon running the code, it always detect the button to be "always pressed" which is not ideal. I need help fixing my code.
Thank you very much.
I'm using EasyPIC fusion v7. The device is P32MX795F512L #80MHz. I'm using the MikroC pro for PIC32 as my IDE."
"Button and UART library is used for this code."
char read;
unsigned int oldstate;
void main() {
AD1PCFG = 0XFFFF; //SETTING AN INPUTS AS DIGITAL I/O
JTAGEN_bit = 0; //disable JTAG
UART2_Init(9600);
Delay_ms(1000);
TRISA = 1; //setting All portA as inputs
UART_Set_Active(&UART2_Read, &UART2_Write, &UART2_Data_Ready, &UART2_Tx_Idle); //Sets UART2 as active
UART_Write_Text("UART is now ready.");
UART_Write(13);
UART_Write(10);
while(1)
{
if (Button(&PORTA, 15, 1 ,1)) //detect if button is pressed
{
UART_Write_Text("Button is pressed.");
UART_Write(13);
UART_Write(10);
oldstate = 1;
}
if (oldstate && Button(&PORTA, 15, 1, 0)) //detect from logic 1 to 0
{
UART_Write_Text("Button is pressed again.");
UART_Write(13);
UART_Write(10);
oldstate = 0;
}
}
}
"I've only learned how UART works and how the Button library works.
I expect the output to be 'Button is pressed' when the button is pressed first, then "Button is pressed again" when I press the same button again.
The output for the code is always 'Button is pressed' and prints continuously"
The main problem with your code is that this line:
UART_Write_Text("Button is pressed again.");
should in fact read:
UART_Write_Text("Button is RELEASED.");
Other than that you might be having a hardware issue if you're not tying your RA15 pin to GND through a pull-down resistor. You could also use the internal pull-ups on your PIC.
The problem is here:
TRISA = 1;
This is simular to TRISA = 0x0001; and will only make the Port A0 an input. You had to write:
TRISA = 0xFFFF;`

How to set Port as Input for Push Button in C?

I'm using MikroC to try and program my PIC16f62 Microcontroller. I've managed to get my outputs working (I can have LEDs turn on, etc) but I can't seem to get the inputs work.
Here is my current code:
void main() {
TRISB.RB0 = 0; //set Port RB0 as output
PORTB.RB0 = 1; //set Port RB0 to high (turn on LED)
TRISA = 1; //Set PORTA as inputs
for(;;){ //endless loop
if(PORTA.RA0 == 1){ //if push button is pressed
PORTB.RB0 = !PORTB.RB0; \\toggle LED
}
}
}
I don't know if the problem is that I'm not configuring the PORT correctly or if I'm checking whether or not the button is pressed incorrectly.
Any help is appreciated. Thanks.
This change may help you.
for(;;){ //endless loop
if(PORTA.RA0 == 1){ //if push button is pressed
PORTB.RB0 = !PORTB.RB0; \\toggle LED
while(PORTA.RA0 == 1);
/*wait till button released as press of a buttons take time and processor is too fast */
}
You are probably reading the port pin correctly, however because you're toggling the LED on and off when you detect a press, your eye can't see the result.
For example, a clock rate of 1Mhz will have the on/off toggle approximately 150,000 times per second (1,000,000 cycles / ~3 ASM instructions per loop / 2 loops to turn on then off).
I would suggest taking the approach of having the LED match the state of the input pin.
for(;;)
{
if(PORTA.RA0 == 1) //if button is pressed
{
PORTB.RB0 = 1; //turn on LED
}
else
{
PORTB.RB0 = 0; //turn off LED
}
}
This technique is similar to what Rajesh suggested, but provides a bit more direct feedback on whether the input pin is set or not.
If that doesn't work, then something with your setup of the TRISA is not correct. You may want to try this:
TRISA.RB0 = 1;

cycling through leds

Please help me with this code, it is making me crazy. This is a very simple program with 8-bit timer, cycling through all 8 leds (one-by-one). Am using ATSTK600 board.
My timers are working well, I think there is some problem with the loops (when I debug this program using avr studio-gcc, I can see all the leds working as I want but when I transfer it on board...leds don't blink). Am going crazy with this type of behavior.
Here is my code:
#include <avr/io.h>
#include <avr/interrupt.h>
volatile unsigned int intrs, i, j = 0;
void enable_ports(void);
void delay(void);
extern void __vector_23 (void) __attribute__ ((interrupt));
void enable_ports()
{
DDRB = 0xff;
TCCR0B = 0x03;
TIMSK0 = 0x01;
//TIFR0 = 0x01;
TCNT0 = 0x00;
//OCR0A = 61;
intrs = 0;
}
void __vector_23 (void)
{
for(i = 0; i<=8; i++)
{
while(1)
{
intrs++;
if(intrs >= 61)
{
PORTB = (0xff<<i);
intrs = 0;
break;
}
}
}
PORTB = 0xff;
}
int main(void)
{
enable_ports();
sei();
while(1)
{
}
}
Your interrupt routine is flawed. intrs counts only the number of times the loop has executed, not the number of timer interrupts as its name suggests. 61 iterations of that loop will take very little time. You will see nothing perceivable without an oscilloscope.
The following may be closer to what you need:
void __vector_23 (void)
{
intrs++;
if(intrs > 60)
{
intrs = 0;
PORTB = (0xff<<i);
i++ ;
if(i == 8 )
{
i = 0 ;
PORTB = 0xff;
}
}
}
Although setting the compare register OCR0A to 61 as in your commented out code would avoid the need for the interrupt counter and reduce unnecessary software overhead.
Are you sure that the code downloaded to the board is not optimized?
Have you attached volatile attribute to the PORTB identifier?
Is there a way for you to slow down the code (outside the debugger)? Any chance it's running but fast that you don't see it?
Can you verify that your intended code is in fact running (outside the debugger)?
When interrupt occurs, handler very quickly counts 62*9 times and finally sets PORTB to 0x00, so leds do only very short flash which is not visible. You see it in sumulator just because it runs slower and do not emulate visual dimming effect of fast port switching. Program has a design flaw: it tries to do full blinking cycle in single interrupt. That's wrong--only a single step should be performed in interrupt call. So handler should look like this:
void __vector_23 (void)
{
intrs++;
if(intrs >= 61)
{
PORTB = (0xff<<i);
intrs = 0;
i++;
if(i>8) i = 0;
}
}
Try this.
There is guidelin on interrupts handlers: Interrupt handler should be as fast and short as possible. Do not perform complex tasks in interrupts (cycle loop is one of them, if you get cycle in interrupt, try to remove it). Do not wait or delay in interrupts.
If you're seeing the behaviour you want when debugging with avr studio-gcc, then that gives you some confidence that your program is "good" (for some sense of the word "good"). So it sounds as though you need to focus on a different area: what is the difference between your debug environment and your stand-alone download?
When doing a stand-alone download, do you know if your program is running at all?
Are the LEDs blinking, or turning on at all? You don't explicitly say in your question, but that question could be very relevant to the debugging process. Does it look like the right behaviour, running at a different speed? If so, then your program is probably not doing some sort of initialisation that the debugger was doing.
When doing a stand-alone download, is the program being compiled with different settings compared to the debug version? Perhaps compiler optimisation settings are changing your program's timing characteristics.
(Your question would be better if you gave more detail about what the stand-alone download is doing. In general, it is hard for someone to debug a remote system when they're given few or no details about what is happening. Do all/some of the LEDs turn on at all?)

Resources