Discussion in 'Programmer's Corner' started by Tigthwad, Feb 5, 2013.

Oct 27, 2009
31
1
My 12 year old son is building a circuit to activate/deactivate a KNEX motor based on a timing circuit (on for ~55 seconds). I am trying to help him get a jump start in understanding how to do it using a 16F628A programming in C

We have been googling a ton and trying to understand how it all works (with a 1 week deadline). We are unable to understand how to use the config settings and the code as written doesn't even turn on the LED yet! The code below is our attempt to turn on LED based on switch input.

The end goal is a program that when powered up turns on LED1 and looks for input from SW1. Upon input from SW1 it should turn on LED2 and delay for 3 seconds while watching SW2 (for cancel). If no input from SW2 in 3 seconds then turn on the Relay for 55 seconds while also activating LED2 to indicate the program is running. After 55 seconds expires go back to watching SW1 with LED1 active.

Currently the test circuit has:

MCLR and VDD tied together with an 8K ohm resistor
VDD tied to 5V input
VSS tied to ground
RB0 to switch to ground
RB1 to switch to ground
RB2 to 330ohm resistor to LED to ground
RB3 to 330ohm resistor to LED to ground

Code ( (Unknown Language)):
1. /************************
2. * Description: LED Test with buttons
3. * Turn on LED for x seconds
4. * Pin Assignments:
5. *   RB0 = Switch 1
6. *    RB1 = Switch 2
7. *    RB2 = LED1
8. *    RB3 = LED2
9. *    RB4 = Relay connection
10.  **************************/
11.
12.
13. #define _XTAL_Frec 4000000
14. #include<xc.h>
15.
16.
17.
18.
19. #define LED_ON      1       // LED is connected cathode to ground
20. #define LED_OFF     0
21.
22. #define RELAY_ON    1       //RELAY is connected to ground
23. #define RELAY_OFF   0       //RELAY not connect to ground
24.
25. #define BUTTON_ON   0       // Button input is low when button pressed
26. #define BUTTON_OFF  1       // (this is how a pull-up resistor button works)
27.
28. #define I_O_OUT     0       // standard TRIS definitions
29. #define I_O_IN      1       // zero is 0ut, one is 1n
30.
31. #define SW_A        RB0     // switch A is on PORTB.0
32. #define SW_B        RB1
33. #define SW_A_TRIS   TRISB0  // switch B is on PORTB.1
34. #define SW_B_TRIS   TRISB1
35.
36. #define LED_A       RB2     // LED A is on PORTB.2
37. #define LED_B       RB3     // LED B is on PORTB.3
38. #define RELAY       RB4     // RELAY is connected to ground
39. #define LED_A_TRIS  TRISB2
40. #define LED_B_TRIS  TRISB3
41. #define RELAY _TRIS TRISB4
42.
43. void main(void)
44. {
45.   SW_A_TRIS  = I_O_IN;    // set up I/O pin directions
46.   SW_B_TRIS  = I_O_IN;
47.   LED_A_TRIS = I_O_OUT;
48.   LED_B_TRIS = I_O_OUT;
49.
50.   // now loop forever to sense buttons
51.   while(1)
52.   {
53.     if (SW_A == BUTTON_ON) // test the first switch
54.         LED_A = LED_ON;
55.     else
56.       LED_A = LED_OFF;
57.
58.     if (SW_B == BUTTON_ON) // test the second switch
59.       LED_B = LED_OFF;
60.     else
61.       LED_B = LED_ON;
62.
63.   }
64. }

2. thatoneguy AAC Fanatic!

Feb 19, 2009
6,357
718
My son picked up on PICAXE at a younger age, the capabilities of which are well within your parameters. They are programmed in BASIC and run fairly fast.

Which C compiler are you using? Are you programming with a PICKit 2/3?

For your program, do you have a 10k Resistor pulling the switch High when it isn't pressed? The PIC will only see 0V or floating (usually read as zero) if there isn't a pullup resistor. Report back if this doesn't solve the problem.

That aside, unrelated to your exact problem, but you may run into it in the future: On ALL PICs that have ADC and comparators, they are ON at power up. You need to disable both the ADC and comparators, otherwise the analog ports will all be Inputs, even if TRIS is set correctly.

Add the following lines to use PortA for digital I/O:
CMCON=0x07;

3. ErnieM AAC Fanatic!

Apr 24, 2011
7,436
1,626
Gee you have a very readable coding style. <grin>

Oct 27, 2009
31
1
We will add the two lines above. Unfortunately I am not very informed on this stuff and its the blind leading the blind. Hopefully his young age will allow him a better starting point!

We are using MPLAB X, XC8 compiler and the Pickit 3 for programming.

The 10K resistor was not part of his circuit and will be added as well for both switches.

If I "borrowed" your code for the basis of this I thank you....it is readable even for my novice eyes and very helpful...hoping we can figure out what else we are missing!

ErnieM likes this.

Oct 27, 2009
31
1
I tried adding ADCON1=0x86; but the code window indicates this is a problem (word is red underline). I added the resistors to the switch ports. Nothing happens when I apply power to the circuit at this point but I haven't reprogrammed the chip as we need to solve the ADCON1 issue.

I tried to understand from the 16F628A datasheet how to set the __config line but I don't understand the options. Which do I need to enable/disable to allow a simple timer and LED lighting?

6. thatoneguy AAC Fanatic!

Feb 19, 2009
6,357
718
You'll want to read the Datasheet for the 16F628A Closely. It is long, but it holds a TON if information.

The config should disable MCLR and enable the Internal Oscillator, usually LVP (Low Voltage Programming) is disabled as well. You'll want to look at the sample files that come with the compiler for a line you can copy that has those settings in it, or find in the help system what the exact keywords are.

The ADCON1 and CMCON only change PortA, since your I/O and LEDs are on PortB, it won't fix the issue. After looking at the datasheet, the 628A doesn't have ADC, so you can leave that line out, just disable the comparators.

If you didn't have pullups on the switches, or the internal oscillator running, that would explain why the test circuit didn't work.

Oct 27, 2009
31
1

Code ( (Unknown Language)):
1. #pragma config MCLRE = OFF, WDTE = OFF, CP = OFF, LVP = OFF, FOSC = INTOSCIO
Did the trick. I wish I understood what it all meant...I get the Code Protection and Watchdog(kinda) and LVP part but I am not firm on the MCLRE and FOSC parts...especially the FOSC as I know I need that later for the delay routine.

Now that the code works, what is the best(or easiest) way to delay for 55 seconds without losing too much time? My son is starting to get excited seeing the LEDs light up!

8. thatoneguy AAC Fanatic!

Feb 19, 2009
6,357
718
There isn't a function for delay in the library functions. Generally, delays are created by running higher priority code. However, for your application, it seems there is a macro, found in the manual:

Oct 27, 2009
31
1
I am unsure how to execute a macro, unfortunately the reading I do doesn't make me feel smarter...

This is what we have built for a delay. It allows us to pass in a time as an int (seconds * 2) and also has some options for getting out of the loop via button_b. The looptuner is used to try to get close to exact timing.

We haven't yet included the relay switching portion but that is next.

Code ( (Unknown Language)):
1. void delay() {
2.     int bigcounter = 0;
3.     int counter = 0;
4.     int looptuner = 19500;
5.     for(bigcounter = 0; bigcounter < 6; bigcounter++)  //set total calue of bigcounter in seconds * 2 - This is our cancel period
6.     {
7.         for(counter = 0; counter < looptuner; counter++)     //approx .5 second delay
8.             {
9.               if (SW_B == BUTTON_ON) break;                 //if switch b is press we exit
10.             }
11.             //LED_B = ~LED_B;
12.             if (SW_B == BUTTON_ON) break;                   //if switch b is press we exit
13.             ;
14.         for(bigcounter = 0; bigcounter < 30; bigcounter++)  //set total calue of bigcounter in seconds * 2
15.         {
16.             for(counter = 0; counter < looptuner; counter++)     //approx .5 second delay
17.             {
18.               if (SW_B == BUTTON_ON) break;                 //if switch b is press we exit
19.             }
20.             LED_B = ~LED_B;
21.             if (SW_B == BUTTON_ON) break;                   //if switch b is press we exit
22.             ;
23.         }
24.     }
25. }

10. thatoneguy AAC Fanatic!

Feb 19, 2009
6,357
718
For a 55 second time delay, I'd suggest interrupts, which will require you to read both the datasheet, the compiler help file, and there are MANY useful App Notes available from Microchip.

As I stated earlier, using a delay() function simply wastes power.

For an interrupt, you do the math to get the right prescaler for the clock you are using, then, when that clock counts down to zero, the interrupt() function (which you'll have to write) is called, which resets the counter, updates flags, and returns to program.

Interrupts are a programmer's best friend, once you learn the usage.

That way, with interrupts, the keypad scanning and display updating is done in real time. PICs run millions of operations per second, while humans can perceive about 100 visual changes per second, and input about 8 characters per second (both on the optimal case). The display update is extremely slow relative to humans, so having the interrupt check for timer, and the main loop checking input and do math, you end up with no "keyboard lag" which takes place when you are trying to change the display parameters at the same time the green light delay is being called.

11. takao21203 Distinguished Member

Apr 28, 2012
3,578
463
You don't have to use full interrupt functions (real interrupt).

You can simply poll the timer interrupt flag which gets set anyway.

I use this sometimes for serial port (USART), because the response is faster and it only requires one line.

The 16F628 is comfortable it also has a 16bit timer (TIMER1).

I don't really understand why people use these macros.

Code ( (Unknown Language)):
1. while(!TMR1IF);
is all what's needed.

And you need to reset each time:
Code ( (Unknown Language)):
1. TMR1IF=0;
The TIMER1 works well in the MPLAB simulator no need to reflash each time.

12. MMcLaren Well-Known Member

Feb 14, 2010
759
116
The XC manual does show a __delay_ms() function;

13. t06afre AAC Fanatic!

May 11, 2009
5,939
1,222
You can use the __delay_ms(x) function in a loop. For the cancel function (SW2). You can say run a 25 msec loop 120 times and check if the SW2 is pressed. If pressed you simply using the break statement to exit from the loop. No need to over complicate things here. And it will be accurate enough. I guess if you loose a few 1/100 sec (in total) in this loop it will not count much
How about the dad, I guess he was also some excited

14. takao21203 Distinguished Member

Apr 28, 2012
3,578
463
The usual solution is a 32 KHz crystal together with the TIMER1 or a RTC chip.

It is also possible to derive it from 4 MHz, 8 MHz or the like, but that's more complicated.

Normally if you want a 55 minutes timer, you want 55 minutes, and not 44 minutes and 50 seconds.

You can use a calculator to see how many seconds that makes, but for simplicity sake, a seconds and minutes mechanism produces better code.

Timers and stopwatches only become really interesting when they have a display. Then you can't use delay anyway.

If the timer is working repeatedly day after day, and you don't use an exact timebase, it will drift slowly through the 24 hours schedule.

The TIMER1 is not that difficult to use or to program.

Oct 27, 2009
31
1
Thanks for the information about interrupts. I knew from my reading that my method was not efficient. At this point we needed something that worked which we have thanks to all the help received thus far. Next we will try to understand how to change the process to be more reliable and precise by using the interrupts. For non-technical people (and 12 year olds with ADD) the datasheets can be quite daunting!

My son is actually excited about learning programming (not just microchips) so this is a perfect project for him to understand that there are multiple ways to do things and some have distinct advantages over others. This means he needs to look for the best, not just the first, answer.

Add relay to circuit and make sure it functions as intended
find out what a prescaler is and how to compute it
find out how an interrupt is incorporated and write them as needed

Oct 27, 2009
31
1
I need to do more reading as I don't understand how to work with TIMER1. The code lines you posted look simple enough but I would need to see them in context to understand what they are doing I think.

I saw this multiple places but when I include this in the code it showed an error...I must not be including something required for it to work. It would be simpler than our loop that is based on just a counter for sure.

If I can figure out how to make the __delay_ms(x) work in the code I will upgrade to that but for now the solution eludes me.

I was excited mostly because I have been pulling my hair out and feeling like a moron for not being able to complete a simple "Hello World" type of program.

Is a 32mhz crystal easier to work with than the included 4mhz in the chip? I have a 32 mhz crystal we could use if needed.

In our case the timer is for 55 seconds only....just turning on a motor circuit and then turning it off again, all on demand so no scheduled off time required. A display would be cool but much more complex for sure.

17. thatoneguy AAC Fanatic!

Feb 19, 2009
6,357
718
Crystals are more accurate than the internal oscillator, it depends if you want a delay of exactly 55.00 seconds (crystal) or somewhere between 54 and 56 seconds (Internal Oscillator). 4Mhz or 20Mhz crystals or oscillators require 3 parts (crystal) or one 4 pin part (oscillator) more on the PCB, close to the PIC. The faster Oscillators, 4M/20M, will give you a more accurate 55 seconds, since a 5% error at 4Mhz reduces a bit after prescaling, while the drift of a 32.768 kHz clock can be extreme if the temperature changes, resulting in RC Oscillator type timing accuracy.

For what you are doing, it sounds like the internal Oscillator will be fine for the effect lite.

18. MMcLaren Well-Known Member

Feb 14, 2010
759
116
When does LED1 get turned off?

After pressing SW1 and turning on LED2 and waiting for 3 seconds, why would you need to turn on LED2 again when you activate the relay? Is this a typo?

19. MMcLaren Well-Known Member

Feb 14, 2010
759
116
It works in my MPLAB 8.84 + XC8 environment...

Code ( (Unknown Language)):
1. /********************************************************************
2.  *                                                                  *
3.  *  Project: 12F1822 Delay Demo                                     *
4.  *   Source: 12F1822 Delay Demo.c                                   *
5.  *   Author: Mike McLaren, K8LH                                     *
6.  *  (C)2013: Micro Application Consultants, All Rights Reserved     *
7.  *     Date: 06-Feb-13                                              *
8.  *                                                                  *
9.  *  12F1822 Delay Demo                                              *
10.  *                                                                  *
11.  *                                                                  *
12.  *      IDE: MPLAB 8.84 (tabs = 4)                                  *
13.  *     Lang: Microchip XC8 v1.11                                    *
14.  *                                                                  *
15.  ********************************************************************/
16.
17.    #include <xc.h>
18.
19.    __CONFIG(FOSC_INTOSC & WDTE_OFF & MCLRE_OFF);
20.    __CONFIG(LVP_OFF & PLLEN_OFF);
21.
22.    #define _XTAL_FREQ 8000000
23.
24.   /******************************************************************
25.    *  function prototypes                                           *
26.    ******************************************************************/
27.   /******************************************************************
28.    *  type definitions                                              *
29.    ******************************************************************/
30.   /******************************************************************
31.    *  variables and constants                                       *
32.    ******************************************************************/
33.   /******************************************************************
34.    *  low level drivers                                             *
35.    ******************************************************************/
36.   /******************************************************************
37.    *  functions                                                     *
38.    ******************************************************************/
39.   /******************************************************************
40.    *  main init                                                     *
41.    ******************************************************************/
42.
43.    void main()
44.    { ANSELA = 0;                // make pins digital
45.      TRISA = 1<<RA1;            // RA1 input, all others output
46.      PORTA = 0;                 // all output latches low
47.      OSCCON = 0b01110010;       // initialize 8-MHz INTOSC
48.      while(!HFIOFS);            // wait until OSC stable
49.
50.   /******************************************************************
51.    *  main loop                                                     *
52.    ******************************************************************/
53.
54.      while(1)                   //
55.      { __delay_ms(200);         //
56.        PORTA ^= 0b00000001;     // toggle RA0 output
57.      }                          //
58.    }
59.

• XC8 delay_ms.png
File size:
44 KB
Views:
411
Last edited: Feb 6, 2013
20. t06afre AAC Fanatic!

May 11, 2009
5,939
1,222
Try this, it is something that your son can understand and maybe improve also The other suggestions are indeed correct. But way over anything what is expected from a 12 year old boy. This may perhaps be cheating. But as I see it. If your son understand this. He has also learned something. Remember this is your sons science fair, not yours
Code ( (Unknown Language)):
1. /************************
2. * Description: LED Test with buttons
3. * Turn on LED for x seconds
4. * Pin Assignments:
5. *   RB0 = Switch 1
6. *    RB1 = Switch 2
7. *    RB2 = LED1
8. *    RB3 = LED2
9. *    RB4 = Relay connection
10.  **************************/
11. #define _XTAL_FREQ 4000000
12. #include<xc.h>
13. #define LED_ON      1       // LED is connected cathode to ground
14. #define LED_OFF     0
15. #define RELAY_ON    1       //RELAY is connected to ground
16. #define RELAY_OFF   0       //RELAY not connect to ground
17. #define BUTTON_ON   0       // Button input is low when button pressed
18. #define BUTTON_OFF  1       // (this is how a pull-up resistor button works)
19. #define I_O_OUT     0       // standard TRIS definitions
20. #define I_O_IN      1       // zero is 0ut, one is 1n
21. #define SW_A        RB0     // switch A is on PORTB.0
22. #define SW_B        RB1
23. #define SW_A_TRIS   TRISB0  // switch B is on PORTB.1
24. #define SW_B_TRIS   TRISB1
25. #define LED_A       RB2     // LED A is on PORTB.2
26. #define LED_B       RB3     // LED B is on PORTB.3
27. #define RELAY       RB4     // RELAY is connected to ground
28. #define LED_A_TRIS  TRISB2
29. #define LED_B_TRIS  TRISB3
30. #define RELAY TRISB4
31. __CONFIG(FOSC_INTOSCIO & WDTE_OFF & PWRTE_ON & MCLRE_OFF & BOREN_OFF & LVP_OFF & CPD_OFF & CP_OFF);
32. unsigned char i;
33. unsigned char flag;
34. void main(void)
35. {
36.   CMCON=0x07;
37.   flag=0;
38.   SW_A_TRIS  = I_O_IN;    // set up I/O pin directions
39.   SW_B_TRIS  = I_O_IN;
40.   LED_A_TRIS = I_O_OUT;
41.   LED_B_TRIS = I_O_OUT;
42.   LED_A=LED_ON;
43.   LED_B=LED_OFF;
44. OSCF=1;
45. /*bit 3 OSCF: INTOSC Oscillator Frequency bit
46. 1 = 4 MHz typical
47. 0 = 48 kHz typical
48. */
49.   // now loop forever to sense buttons
50.   while(1)
51.   {
52.       while(SW_A==1);
53.       LED_B=LED_ON;
54.       for(i=0;i<40;i++)
55.         {
56.           if (SW_B==0)
57.           {
58.               LED_B=LED_OFF;
59.               flag=1;
60.               break;
61.           }
62.           __delay_ms(75);
63.           LED_B=!LED_B;
64.          }
65.        if(flag==0)
66.        {
67.           LED_B=LED_ON;
68.           RELAY=RELAY_ON;
69.           for(i=0;i<55;i++)
70.           {
71.               __delay_ms(1000);
72.           }
73.           RELAY=RELAY_OFF;
74.        }
75.        LED_B=0;
76.        flag=0;
77.   }
78. }//end main

File size:
12.2 KB
Views:
27