Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

Switch Statement for PIC Microcontroller

Status
Not open for further replies.

alpha91

Full Member level 3
Joined
Sep 23, 2011
Messages
168
Helped
1
Reputation
2
Reaction score
2
Trophy points
1,298
Activity points
2,625
Hi all, I am learning Switch Statement in C currently and i wrote a code for testing with MikroC.
The code is compile and no error but it just now working. Can anyone help me to check what's my mistake here?

Function of the code:
When button at Port A0 is pressed, it should change the LED output style.

My code is as below:

unsigned short num;
unsigned i;
void main() {
CMCON = 0x07;
TRISB = 0x00;
TRISA = 0xFF;
num = 1;
while (1){
switch (num){

case 1: PORTB = 0x01;
delay_ms(300);
PORTB = 0x00;
delay_ms(300);
break;

case 2: PORTB = 0x02;
delay_ms(300);
PORTB = 0x00;
delay_ms(300);
break;

case 3: PORTB = 0x03;
delay_ms(300);
PORTB = 0x00;
delay_ms(300);
break;
}

if (PORTA.F0 == 1) {
delay_ms(50);
if (PORTA.F0 == 1) {
i++;
num = i;
}
}
}

}

i checked my hardware. everything is fine.
 

You should not necessarily create a temporary variable "num". The variable "i" itself could be used as the switch argument. Anyway, the code as wrote will not work as expected, because you did not provide any limit to the counting. You could replace the "i++" statement by: "if (i++ > 3) i=0"
 
You do not say which PIC you are using, but if it has analogue function available on PORTA, then you will need to disable analogue as described in the datasheet for your PIC
 
You should not necessarily create a temporary variable "num". The variable "i" could be used as the switch argument. Anyway, the code as wrote will not work as expected, because you did not provided any limit to the counting. You could replace the "i++" statement by: "if (i++ > 3) i=0"

i see. thanks for your advice. it works now. :thumbsup:
but i need to pressed a bit longer for the button to change the status.
even i tried to reduce the switch de-bounce delay but still the same.


You do not say which PIC you are using, but if it has analogue function available on PORTA, then you will need to disable analogue as described in the datasheet for your PIC

sorry, my mistake. I am using 16F628a.
I had disabled the comparator with CMCON = 0x07 command
 
Last edited:

You have the entire PORTA set to inputs. Do you have pullup resistors on all of those pins? If not, they're floating, which is not good. Unused pins should either be set to output and driven low, or set to input and tied high or low. Most peope opt for setting them to outputs.

What does your circuit look like at the button? Most people would tie the pin high via a 10K resistor and have the button pull the pin low.
 
You have the entire PORTA set to inputs. Do you have pullup resistors on all of those pins? If not, they're floating, which is not good. Unused pins should either be set to output and driven low, or set to input and tied high or low. Most peope opt for setting them to outputs.

What does your circuit look like at the button? Most people would tie the pin high via a 10K resistor and have the button pull the pin low.

Hi, i am sorry that i forgot to change the PORTA setting. i did this for switch troubleshooting.
i did pulled down with 10k resistor.
Now my code is working. but the thing is i need to pressed the switch for 2 seconds in order to change the status. Still trying to solve this. :thinker:
 

i need to pressed the switch for 2 seconds in order to change the status. Still trying to solve this

With the code as wrote, you are running a sequential LED blink routine, spending 600ms at each one of the 3 outputs, so just after 1,8 seconds will will be able to read the button. You need to avoid using Delay() functions. Take a look on examples of delays using Timer interruption, so that you can do other stuffs meanwhile.
 
As andre_teprom said the delays are causing the problem. When delay is executing the button press will not be detected. You have to use Timer Interrupt for making the delay of 300 ms and toggle the required bit of the port in while(1) loop.
 
With the code as wrote, you are running a sequential LED blink routine, spending 600ms at each one of the 3 outputs, so just after 1,8 seconds will will be able to read the button. You need to avoid using Delay() functions. Take a look on examples of delays using Timer interruption, so that you can do other stuffs meanwhile.

As andre_teprom said the delays are causing the problem. When delay is executing the button press will not be detected. You have to use Timer Interrupt for making the delay of 300 ms and toggle the required bit of the port in while(1) loop.


i see...That is the INTCON instruction right?
thanks for the information, i will look into it.
 

Here is a project which I made. It is similar to your project. Try it. It uses Timer Interrupt for the delay of 300 ms. Based on the button counter value the switch statement in the ISR selects which PORTB pin to toggle. Proteus file is included and also Proteus simulation video is included. You need Proteus 8.5 SP0 or higher to view the Proteus Simulation file.
 

Attachments

  • LED Pattern.rar
    162.4 KB · Views: 144
I modified the code a little. Here is the new code. I replaced the signed char counter variable with a unsigned char type and eliminated the use of a negative value for the counter.


Code C - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
sbit SW at RA0_bit;
 
unsigned char pattern_counter = 0;
 
//Timer1
//Prescaler 1:8; TMR1 Preload = 28036; Actual Interrupt Time : 300 ms
//Place/Copy this part in declaration section
void InitTimer1() {
    T1CON = 0x31;
    TMR1IF_bit = 0;
    TMR1H = 0x6D;
    TMR1L = 0x84;
    TMR1IE_bit = 1;
    INTCON = 0xC0;
}
 
void Interrupt() {
    if(TMR1IF_bit) {
        //Enter your code here
        switch(pattern_counter) {
            case 0:
                  break;
            case 1:
                  RB0_bit = ~RB0_bit;
                  break;
            case 2:
                  RB1_bit = ~RB1_bit;
                  break;
            case 3:
                  RB2_bit = ~RB2_bit;
                  break;
        };
 
        TMR1IF_bit = 0;
        TMR1H = 0x6D;
        TMR1L = 0x84;
    }
}
 
void main() {
 
    CMCON = 0x07;
    
    TRISA = 0b11100001;
    TRISB = 0x00;
    
    PORTA = 0x01;
    PORTB = 0x00;
    
    InitTimer1();
    
    while(1) {
    
        if(SW == 1) {
            Delay_ms(50);
            while(SW == 1);
            
            if(++pattern_counter > 3)pattern_counter = 0;
            PORTB = 0x00;
        }
    }
}

 
I modified the code a little. Here is the new code. I replaced the signed char counter variable with a unsigned char type and eliminated the use of a negative value for the counter.


Code C - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
sbit SW at RA0_bit;
 
unsigned char pattern_counter = 0;
 
//Timer1
//Prescaler 1:8; TMR1 Preload = 28036; Actual Interrupt Time : 300 ms
//Place/Copy this part in declaration section
void InitTimer1() {
    T1CON = 0x31;
    TMR1IF_bit = 0;
    TMR1H = 0x6D;
    TMR1L = 0x84;
    TMR1IE_bit = 1;
    INTCON = 0xC0;
}
 
void Interrupt() {
    if(TMR1IF_bit) {
        //Enter your code here
        switch(pattern_counter) {
            case 0:
                  break;
            case 1:
                  RB0_bit = ~RB0_bit;
                  break;
            case 2:
                  RB1_bit = ~RB1_bit;
                  break;
            case 3:
                  RB2_bit = ~RB2_bit;
                  break;
        };
 
        TMR1IF_bit = 0;
        TMR1H = 0x6D;
        TMR1L = 0x84;
    }
}
 
void main() {
 
    CMCON = 0x07;
    
    TRISA = 0b11100001;
    TRISB = 0x00;
    
    PORTA = 0x01;
    PORTB = 0x00;
    
    InitTimer1();
    
    while(1) {
    
        if(SW == 1) {
            Delay_ms(50);
            while(SW == 1);
            
            if(++pattern_counter > 3)pattern_counter = 0;
            PORTB = 0x00;
        }
    }
}



Thank you for your code.
I will study it and try it out.
 

I modified the code a little. Here is the new code. I replaced the signed char counter variable with a unsigned char type and eliminated the use of a negative value for the counter.


Hi, i had edited my code as below:


Code C - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
unsigned short num;
unsigned i;
 
void InitTimer1() {
    T1CON = 0x31;
    TMR1IF_bit = 0;
    TMR1H = 0x6D;
    TMR1L = 0x84;
    TMR1IE_bit = 1;
    INTCON = 0xC0;
}
void Interrupt() {
   if(TMR1IF_bit) {
          switch (num)
           {
           case 1:PORTB = 0x01;
           delay_ms(300);
           PORTB = 0x00;
           delay_ms(300);
           break;
 
           case 2:PORTB = 0x02;
           delay_ms(300);
           PORTB = 0x00;
           delay_ms(300);
           break;
 
           case 3:PORTB = 0x03;
           delay_ms(300);
           PORTB = 0x00;
           delay_ms(300);
           break;
           }    // case end
              
           TMR1IF_bit = 0;
       TMR1H = 0x6D;
       TMR1L = 0x84;
    }
}                
void main() {
     CMCON = 0x07;
     TRISB = 0x00;
     TRISA = 0xFF;
     InitTimer1();
     num = 1;
                                             // initialize status
            while (1){
                         if (PORTA.F0 == 1)
                         {
                         if (i++ > 3) i=1;
                         num = i;
                         }
 }
}




But the mode is changed without me pressed the button.
I am still trying to edit it. Does it matter with the compiler?
I am using MikroC.
 
You still lacks of fundamental concepts on the proper usage of the main routine and the interrupt routine. As for example, you are now using long delays, and worse, apparently within the interrupt vector. You should consider the code that Okada suggested.

Have a look here: https://en.wikibooks.org/wiki/Microprocessor_Design/Interrupts
An interrupt is a condition that causes the microprocessor to temporarily work on a different task

You're doing the opposite.
 
Hi

As andre_tepro suggested please don't use delays in interrupt service routine (isr). The timer interrupt is used to generate 300 ms delay. You have to just toggle the required PORTB pin based on the counter value. Also in your code value of variable num is 1 before entering while(1) loop nd this will make RB0 LED to blink as soon as the device is turned on because InitTimer1() is called before while(1) loop and the timer is ON all the time ( same in my code but I use counter value 0 to keep all pins turned off).

There was one bug in my code. Please change

Code:
PORTA = 0x01;

to

Code:
PORTA = 0x00;


And where did the debounce delay go in the button press detect code ?

This is a tested code which I made from your code and it is working.


Code C - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
unsigned char num = 0;
 
//Timer1
//Prescaler 1:8; TMR1 Preload = 28036; Actual Interrupt Time : 300 ms
//Place/Copy this part in declaration section
void InitTimer1() {
    T1CON = 0x31;
    TMR1IF_bit = 0;
    TMR1H = 0x6D;
    TMR1L = 0x84;
    TMR1IE_bit = 1;
    INTCON = 0xC0;
}
 
void Interrupt() {
    if(TMR1IF_bit) {
        //Enter your code here
        switch(num) {
            case 0:
                  break;
            case 1:
                  RB0_bit = ~RB0_bit;
                  break;
            case 2:
                  RB1_bit = ~RB1_bit;
                  break;
            case 3:
                  RB2_bit = ~RB2_bit;
                  break;
        };
 
        TMR1IF_bit = 0;
        TMR1H = 0x6D;
        TMR1L = 0x84;
    }
}
 
void main() {
 
    CMCON = 0x07;
 
    TRISA = 0b11100001;
    TRISB = 0x00;
 
    PORTA = 0x00;
    PORTB = 0x00;
 
    InitTimer1();
 
    while(1) {
 
        if(PORTA.F0 == 1) {
            Delay_ms(50);
            while(PORTA.F0 == 1);
 
            if(++num > 3)num = 0;
            PORTB = 0x00;
        }
    }
}

 
Last edited:
If you are not yet confortable building delays based on Timer interrupt, another option that you could consider on the original closed loop Delay() approach, is using pin-change interrupt. You did not mention for which PIC familly you made this code, but at least on the 16F core, the PORTB has this funtionality. On this case, the button read is done by sensing changes on a specific pin of this port.
 
  • Like
Reactions: alpha91

    V

    Points: 2
    Helpful Answer Positive Rating

    alpha91

    Points: 2
    Helpful Answer Positive Rating
If you are not yet confortable building delays based on Timer interrupt, another option that you could consider on the original closed loop Delay() approach, is using pin-change interrupt. You did not mention for which PIC familly you made this code, but at least on the 16F core, the PORTB has this funtionality. On this case, the button read is done by sensing changes on a specific pin of this port.

Hi, i am using PIC16F628a.
actually i am still learning all the function of PIC. I do understand what is interrupt.
Just that i dont know how i should write it in coding.
For example, i havent fully understand how INTCON works, from bit 0 to bit 7, it has its own function.
And i am ok for either way because i want to learn.
I am studying the interrupt timer that Okada suggested, just that a bit tough and takes some time for me.

May i know what is the instructions that you suggested? So that i can search for some material to study.
Thank you.

- - - Updated - - -

Hi

As andre_tepro suggested please don't use delays in interrupt service routine (isr). The timer interrupt is used to generate 300 ms delay. You have to just toggle the required PORTB pin based on the counter value. Also in your code value of variable num is 1 before entering while(1) loop nd this will make RB0 LED to blink as soon as the device is turned on because InitTimer1() is called before while(1) loop and the timer is ON all the time ( same in my code but I use counter value 0 to keep all pins turned off).

There was one bug in my code. Please change

Code:
PORTA = 0x01;

to

Code:
PORTA = 0x00;


And where did the debounce delay go in the button press detect code ?

This is a tested code which I made from your code and it is working.


Code C - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
unsigned char num = 0;
 
//Timer1
//Prescaler 1:8; TMR1 Preload = 28036; Actual Interrupt Time : 300 ms
//Place/Copy this part in declaration section
void InitTimer1() {
    T1CON = 0x31;
    TMR1IF_bit = 0;
    TMR1H = 0x6D;
    TMR1L = 0x84;
    TMR1IE_bit = 1;
    INTCON = 0xC0;
}
 
void Interrupt() {
    if(TMR1IF_bit) {
        //Enter your code here
        switch(num) {
            case 0:
                  break;
            case 1:
                  RB0_bit = ~RB0_bit;
                  break;
            case 2:
                  RB1_bit = ~RB1_bit;
                  break;
            case 3:
                  RB2_bit = ~RB2_bit;
                  break;
        };
 
        TMR1IF_bit = 0;
        TMR1H = 0x6D;
        TMR1L = 0x84;
    }
}
 
void main() {
 
    CMCON = 0x07;
 
    TRISA = 0b11100001;
    TRISB = 0x00;
 
    PORTA = 0x00;
    PORTB = 0x00;
 
    InitTimer1();
 
    while(1) {
 
        if(PORTA.F0 == 1) {
            Delay_ms(50);
            while(PORTA.F0 == 1);
 
            if(++num > 3)num = 0;
            PORTB = 0x00;
        }
    }
}


Hi, i roughly understand now.
So is it means that i dont use delay now, and to replace the delay, i need to use timer. am i right?

and i have one more question. May i know why the PORT A as below?

TRISA = 0b11100001;
 
Last edited:

Hi

So is it means that i dont use delay now, and to replace the delay, i need to use timer. am i right?

Yes. The comment of the InitTimer1() shows that it is configured for 300 ms and so the ISR (Interrupt Service Routine) is called once every 300 ms.

RA6 and RA7 are for Oscillator and RA5 is for MCLR and hence configured as inputs in TRISA. RA0 is for Button and also configured as input pin.

See attached Circuit.

What andre_teprom suggestes is to retain the delay_ms() based code in the while(1) loop and use INTIE for the button and increment the pattern_counter (num) inside the INTIF interrupt.

- - - Updated - - -

Here is the project which uses INTE (External Interrupt) for button press detection as mentioned by andre_teprom and here is its mikroC PRO code. See attached video for working of the project.


Code C - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
sbit LED1 at RB1_bit;
sbit LED2 at RB2_bit;
sbit LED3 at RB3_bit;
 
unsigned char num = 0;
 
void Interrupt() {
    if(INTF_bit) {
        if(++num > 3) {
           num = 0;
        }
        
        PORTB = 0x00;
        INTF_bit = 0;
    }
}
 
void main() {
 
    CMCON = 0x07;
 
    TRISA = 0b11100000;
    TRISB = 0x01;
 
    PORTA = 0x00;
    PORTB = 0x00;
 
    INTEDG_bit = 1;
    INTE_bit = 1;
    PEIE_bit = 1;
    GIE_bit = 1;
 
    while(1) {
 
        switch(num) {
            case 1:
                  RB1_bit = ~RB1_bit;
                  Delay_ms(300);
                  break;
            case 2:
                  RB2_bit = ~RB2_bit;
                  Delay_ms(300);
                  break;
            case 3:
                  RB3_bit = ~RB3_bit;
                  Delay_ms(300);
                  break;
        }
    }
}

 

Attachments

  • Circuit.png
    Circuit.png
    26.2 KB · Views: 290
  • Circuit.png
    Circuit.png
    22.7 KB · Views: 197
  • LED Pattern Using External Interrupt.rar
    84.6 KB · Views: 128
Last edited:

Hi



Yes. The comment of the InitTimer1() shows that it is configured for 300 ms and so the ISR (Interrupt Service Routine) is called once every 300 ms.

RA6 and RA7 are for Oscillator and RA5 is for MCLR and hence configured as inputs in TRISA. RA0 is for Button and also configured as input pin.

See attached Circuit.

What andre_teprom suggestes is to retain the delay_ms() based code in the while(1) loop and use INTIE for the button and increment the pattern_counter (num) inside the INTIF interrupt.

- - - Updated - - -

Here is the project which uses INTE (External Interrupt) for button press detection as mentioned by andre_teprom and here is its mikroC PRO code. See attached video for working of the project.

i see. In all of my previous code, i didn't set the TRISA as you even though i used them as oscillator and MCLR, that's why i am curious to ask.

thank you very much for your hard work and kindness. I will study all of these.
If i have any further question, i will ask here again. Thank you :thumbsup:
 

Hi everyone,


I am studying about the timer interrupt and there is one part which I don't really understand.
May I know what is TM1H and TMR1L function? ( from Okada code is set at TMR1H = 0x06D and TMR1L = 0x85)
I know they are two 8 bit register in TMR1 but I don't understand what is the register function.

Another question is, what is the prescaler for?
How to determine the value that i should set?
 

Status
Not open for further replies.

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top