Thursday, October 7, 2010
These are going to be among my favorite PICks
Product brochure for the PIC 12F/16F18xx. Problem with these chips is that there's hardly any C compiler for them. As far as I know Microchip's own Hi Tech C doesn't support any of them yet. Can somebody please kick their butts?
Thursday, September 30, 2010
Hi-Tech C vs mikroC Pro
Just out of curiosity I compiled a small sample program on Hi-Tech C for PIC18 v9.63L3 and mikroC Pro v4.10 freeware version. Target MCU was a PIC18F2321. Hi-Tech was used within Microchip's MPLAB IDE v.8.30. Hi-Tech's Omniscient Code Generation (OCG) is on (for the duration of the 45-day eval period). I set the mikroC optimization level to 5 (the maximum).
According to the compilers the Hi-Tech version took up 84 bytes of Flash while the mikroC required 122 bytes. Both compilers used only 2 bytes of RAM. Apparently Hi-Tech wins by a mile, at least in this particular test.
Here's the C program I fed both compilers:
Here's the disaasembly listing by Hi-Tech:
Disassembly listing by mikroC:
According to the compilers the Hi-Tech version took up 84 bytes of Flash while the mikroC required 122 bytes. Both compilers used only 2 bytes of RAM. Apparently Hi-Tech wins by a mile, at least in this particular test.
Here's the C program I fed both compilers:
#define int8 unsigned char void InitRegisters() { IRCF1 = 1; ADCON1 = 0b1111; // turn all analog inputs into digital PORTA = 0; PORTB = 0; PORTC = 0; TRISA = 0; TRISB = 0; TRISC = 0; } void OnLoad(int8 *salad) { *salad = 0xFF; } void OffLoad(int8 *greens) { *greens = 0; } // ==================================================================== // main // ==================================================================== void main() { InitRegisters(); while(1) { if (PORTC) OffLoad(&PORTC); else OnLoad(&PORTC); } // while (1) } // main
Here's the disaasembly listing by Hi-Tech:
1: /* 2: 3: sandbox 4: compiler = Hi Tech 5: 6: */ 7: 8: #include9: 10: __CONFIG (1, INTIO & FCMDIS & IESODIS); 11: __CONFIG (2, PWRTDIS & BORDIS & WDTDIS & WDTPS4); 12: __CONFIG (3, PBDIGITAL & LPT1DIS & MCLRDIS); 13: __CONFIG (4, XINSTDIS & STVRDIS & LVPDIS & DEBUGDIS); 14: __CONFIG (5, 0xFFFF); // write protect disabled 15: __CONFIG (6, 0xFFFF); // write protect disabled 16: __CONFIG (7, 0xFFFF); // write protect disabled 17: 18: 19: #define int8 unsigned char 20: 21: // ==================================================================== 22: // Global variables and enumerations 23: // ==================================================================== 24: 25: 26: 27: 28: // ==================================================================== 29: // Functions 30: // ==================================================================== 31: 32: void InitRegisters() 33: { 34: IRCF1 = 1; 1FC8 8AD3 BSF 0xfd3, 0x5, ACCESS 35: ADCON1 = 0b1111; // turn all analog inputs into digital 1FCA 0E0F MOVLW 0xf 1FCC 6EC1 MOVWF 0xfc1, ACCESS 36: 37: PORTA = 0; 1FCE 6A80 CLRF 0xf80, ACCESS 38: PORTB = 0; 1FD0 6A81 CLRF 0xf81, ACCESS 39: PORTC = 0; 1FD2 6A82 CLRF 0xf82, ACCESS 40: TRISA = 0; 1FD4 6A92 CLRF 0xf92, ACCESS 41: TRISB = 0; 1FD6 6A93 CLRF 0xf93, ACCESS 42: TRISC = 0; 1FD8 6A94 CLRF 0xf94, ACCESS 43: } 1FDA 0012 RETURN 0 44: 45: void OnLoad(int8 *salad) 46: { 47: *salad = 0xFF; 1FBC C000 MOVFF 0, 0xfe9 1FBE FFE9 NOP 1FC0 C001 MOVFF 0x1, 0xfea 1FC2 FFEA NOP 1FC4 68EF SETF 0xfef, ACCESS 48: } 1FC6 0CFF RETLW 0xff 49: 50: void OffLoad(int8 *greens) 51: { 52: *greens = 0; 1FB0 C000 MOVFF 0, 0xfe9 1FB2 FFE9 NOP 1FB4 C001 MOVFF 0x1, 0xfea 1FB6 FFEA NOP 1FB8 6AEF CLRF 0xfef, ACCESS 53: } 1FBA 0C00 RETLW 0 54: 55: // ==================================================================== 56: // main 57: // ==================================================================== 58: 59: void main() 60: { 61: InitRegisters(); 1FDC ECE4 CALL 0x1fc8, 0 1FDE F00F NOP 62: 63: while(1) 1FFE D7F0 BRA 0x1fe0 64: { 65: if (PORTC) 1FE0 5082 MOVF 0xf82, W, ACCESS 1FE2 E007 BZ 0x1ff2 66: OffLoad(&PORTC); 1FE4 0E82 MOVLW 0x82 1FE6 6E00 MOVWF 0, ACCESS 1FE8 0E0F MOVLW 0xf 1FEA 6E01 MOVWF 0x1, ACCESS 1FEC ECD8 CALL 0x1fb0, 0 1FEE F00F NOP 1FF0 D7F7 BRA 0x1fe0 67: else 68: OnLoad(&PORTC); 1FF2 0E82 MOVLW 0x82
Disassembly listing by mikroC:
; LST file generated by mikroListExporter - v.2.0 ; Date/Time: 9/30/2010 8:21:34 PM ;---------------------------------------------- ;Address Opcode ASM 0x0000 0xF000EF2D GOTO 90 0x0004 0x0000 NOP 0x0006 0x0000 NOP 0x0008 0xF000EF00 GOTO 0 0x000C 0x0000 NOP 0x000E 0x0000 NOP 0x0010 0x0000 NOP 0x0012 0x0000 NOP 0x0014 0x0000 NOP 0x0016 0x0000 NOP 0x0018 0xD7F3 BRA 0 _OnLoad: ;sandbox18F.c,36 :: void OnLoad(int8 *salad) ;sandbox18F.c,38 :: *salad = 0xFF; 0x001C 0xFFE1C015 MOVFF FARG_OnLoad_salad, FSR1L 0x0020 0xFFE2C016 MOVFF FARG_OnLoad_salad+1, FSR1H 0x0024 0x0EFF MOVLW 255 0x0026 0x6EE6 MOVWF POSTINC1 ;sandbox18F.c,39 :: } 0x0028 0x0012 RETURN 0 ; end of _OnLoad ___CC2DW: ;__Lib_System.c,21 :: ;__Lib_System.c,23 :: _CC2DL_Loop1: ;__Lib_System.c,24 :: 0x002A 0x0009 TBLRD*+ ;__Lib_System.c,25 :: 0x002C 0xFFE6CFF5 MOVFF TABLAT, POSTINC1 ;__Lib_System.c,26 :: 0x0030 0x0600 DECF R0, 1, 0 ;__Lib_System.c,27 :: 0x0032 0xE1FB BNZ _CC2DL_Loop1 ;__Lib_System.c,28 :: 0x0034 0x0601 DECF R1, 1, 0 ;__Lib_System.c,29 :: 0x0036 0xE1F9 BNZ _CC2DL_Loop1 ;__Lib_System.c,31 :: 0x0038 0x0012 RETURN 0 ; end of ___CC2DW _OffLoad: ;sandbox18F.c,41 :: void OffLoad(int8 *greens) ;sandbox18F.c,43 :: *greens = 0; 0x003A 0xFFE1C015 MOVFF FARG_OffLoad_greens, FSR1L 0x003E 0xFFE2C016 MOVFF FARG_OffLoad_greens+1, FSR1H 0x0042 0x6AE6 CLRF POSTINC1 ;sandbox18F.c,44 :: } 0x0044 0x0012 RETURN 0 ; end of _OffLoad _InitRegisters: ;sandbox18F.c,23 :: void InitRegisters() ;sandbox18F.c,25 :: OSCCON.IRCF1 = 1; 0x0046 0x8AD3 BSF OSCCON, 5 ;sandbox18F.c,26 :: ADCON1 = 0b1111; // turn all analog inputs into digital 0x0048 0x0E0F MOVLW 15 0x004A 0x6EC1 MOVWF ADCON1 ;sandbox18F.c,28 :: PORTA = 0; 0x004C 0x6A80 CLRF PORTA ;sandbox18F.c,29 :: PORTB = 0; 0x004E 0x6A81 CLRF PORTB ;sandbox18F.c,30 :: PORTC = 0; 0x0050 0x6A82 CLRF PORTC ;sandbox18F.c,31 :: TRISA = 0; 0x0052 0x6A92 CLRF TRISA ;sandbox18F.c,32 :: TRISB = 0; 0x0054 0x6A93 CLRF TRISB ;sandbox18F.c,33 :: TRISC = 0; 0x0056 0x6A94 CLRF TRISC ;sandbox18F.c,34 :: } 0x0058 0x0012 RETURN 0 ; end of _InitRegisters _main: ;sandbox18F.c,50 :: void main() ;sandbox18F.c,52 :: InitRegisters(); 0x005A 0xDFF5 RCALL _InitRegisters ;sandbox18F.c,54 :: while(1) L_main0: ;sandbox18F.c,56 :: if (PORTA) 0x005C 0x5280 MOVF PORTA, 1 0x005E 0xE006 BZ L_main2 ;sandbox18F.c,57 :: OffLoad(&PORTA); 0x0060 0x0E80 MOVLW PORTA 0x0062 0x6E15 MOVWF FARG_OffLoad_greens 0x0064 0x0E0F MOVLW hi_addr(PORTA) 0x0066 0x6E16 MOVWF FARG_OffLoad_greens+1 0x0068 0xDFE8 RCALL _OffLoad 0x006A 0xD005 BRA L_main3 L_main2: ;sandbox18F.c,59 :: OnLoad(&PORTA); 0x006C 0x0E80 MOVLW PORTA 0x006E 0x6E15 MOVWF FARG_OnLoad_salad 0x0070 0x0E0F MOVLW hi_addr(PORTA) 0x0072 0x6E16 MOVWF FARG_OnLoad_salad+1 0x0074 0xDFD3 RCALL _OnLoad L_main3: ;sandbox18F.c,60 :: } // while (1) 0x0076 0xD7F2 BRA L_main0 ;sandbox18F.c,61 :: } // main 0x0078 0xD7FF BRA $+0 ; end of _main
Tuesday, September 28, 2010
The PIC16F1827: an inexpensive, fully loaded MCU
I'm excited about the PIC16F1827 because it's packed with peripherals, has five! timers, has an internal clock with a maximum frequency of 32MHz, and yet is no more expensive than other 18pin PIC16s. I want a tube of these!
Just my luck though. I'll have to wait. I try and obtain all my PICs from Microchip Direct, but the 1827 won't be available till the end of this year (extended temperature PDIP is on stock, however). RS Components has none either. Farnell has stocks but comes out way pricier.
Hardware aside, I have no plans on programming this chip using assembly. So I need a C compiler. You'd expect Microchip's compiler division would have a compiler ready once the chip is out, but even with the acquisition of Hi Tech it has yet to support the 1827. That's rather strange. The just released MikroC Pro v4.10 does support the MCU and several other enhanced PIC16s. BoostC by SourceBoost also recognizes the 1827.
Guess I'll be playing with this MCU early next year.
Just my luck though. I'll have to wait. I try and obtain all my PICs from Microchip Direct, but the 1827 won't be available till the end of this year (extended temperature PDIP is on stock, however). RS Components has none either. Farnell has stocks but comes out way pricier.
Hardware aside, I have no plans on programming this chip using assembly. So I need a C compiler. You'd expect Microchip's compiler division would have a compiler ready once the chip is out, but even with the acquisition of Hi Tech it has yet to support the 1827. That's rather strange. The just released MikroC Pro v4.10 does support the MCU and several other enhanced PIC16s. BoostC by SourceBoost also recognizes the 1827.
Guess I'll be playing with this MCU early next year.
Friday, September 24, 2010
On the blink
James Bryant of Analog Devices and his dirty dozen ways for circuits to fail.
The PIR circuit I recently installed has been challenging me. Just two days after it was installed it began to at times fail to switch the load on even if the PIR sensor was detecting motion (I know because the LED had turned on, ergo, PIR output was high). The fault was infrequent and usually occurred the first time the pantry was accessed in the morning--circuit must be too groggy to start the day. After days of checking and monitoring supply voltages (and finding nothing wrong with them), I decided to reprogram the MCU. The problem disappeared. Could've been an intermittent contact problem--which was licked when I removed the MCU and reinserted it--or it could've been a bad burn of the firmware.
Another problem which was noticed along with the above has yet to be resolved. When the dining room lights are turned off, the pantry light sometimes switches on. This occurs probably once every couple of dozen switchings. Rather obviously transients in the power line from switching off four conventionally ballasted plug-in type 11-watt compact fluorescent lamps are falsely triggering the circuit. I've already monitored MCU VDD using the Min-Max with Peak detect feature of the Fluke 87V for some 24hours and found the voltage to be from 4.930V to 5.048V, so no problem there. On the other hand, given the cable length to the PIR module of some 40cm, the problem may lie there. That's No.5 in Bryant's list. I've been mullling where in the sensor circuit to install additional bypass/decoupling caps. May have to put one at the VDD of the module and another at the MCU input pin (GP3); 0.1 to 1uF for the former and 10 to 100nF for the latter.
The PIR circuit I recently installed has been challenging me. Just two days after it was installed it began to at times fail to switch the load on even if the PIR sensor was detecting motion (I know because the LED had turned on, ergo, PIR output was high). The fault was infrequent and usually occurred the first time the pantry was accessed in the morning--circuit must be too groggy to start the day. After days of checking and monitoring supply voltages (and finding nothing wrong with them), I decided to reprogram the MCU. The problem disappeared. Could've been an intermittent contact problem--which was licked when I removed the MCU and reinserted it--or it could've been a bad burn of the firmware.
Another problem which was noticed along with the above has yet to be resolved. When the dining room lights are turned off, the pantry light sometimes switches on. This occurs probably once every couple of dozen switchings. Rather obviously transients in the power line from switching off four conventionally ballasted plug-in type 11-watt compact fluorescent lamps are falsely triggering the circuit. I've already monitored MCU VDD using the Min-Max with Peak detect feature of the Fluke 87V for some 24hours and found the voltage to be from 4.930V to 5.048V, so no problem there. On the other hand, given the cable length to the PIR module of some 40cm, the problem may lie there. That's No.5 in Bryant's list. I've been mullling where in the sensor circuit to install additional bypass/decoupling caps. May have to put one at the VDD of the module and another at the MCU input pin (GP3); 0.1 to 1uF for the former and 10 to 100nF for the latter.
Wednesday, September 22, 2010
An important pointer on using mikroC with PIC18s
After half a day of trying to get it to work I nearly gave up. Was trying out C pointers using an old version of mikroC on a Microchip PIC18F2321. Here's the test program (to preclude clutter I've removed the initialization routines for OSCCON, PORTx, TRISx, and ADCON1 registers):
The firware merely toggles PORTC on and off every tenth of a second. An LED is connected to one of the PORTC pins, so it should blink when the MCU is powered up. Well, it didn't work.
After a couple of hours of racking my brain and trying out variations on the program, I decided to test the code on a PIC 16F device. I inserted a PIC12F615 into the breadboard, plugged in an LED and resistor, and hooked up the PICKit 2. Opened a new project in mikroC and used the same program, changing PORTx to GPIO, and initializing the appropriate SFRs. Guess what? Worked the first time around. No problems. That told me there's nothing wrong with the program. Went back to the PIC18.
After several more hours of checking and hair-pulling, it occured to me that there might be something in the MCU configuration bits which hasn't been set properly. Referring to the datasheet, the only one that seemed relevant was the XINST (Extended Instruction Set enable) bit in configuration word CONFIG4L. Noting that according to the datashet it's off by default, I first enabled it. I compiled the firmware, uploaded to the MCU, and powered it up. Nothing. The program still wouldn't work. Went back to mikroC, unchecked the "XINST_ON_4L" box and checked "XINST_OFF_4L."
Hallelujah! The LED finally started blinking, and at the proper rate at that. Finally found the culprit! Just to make sure it was indeed the XINST bit, I enabled it again. Sure enough the program failed again. Disabling XINST once more and the firmware worked. As a final test, I unchecked both these boxes (it was in that condition before I fiddled around with the XINST) and uploaded the compiled code. It didn't work. Therefore, regardless of what Microchip says about the default condition of the configuration words, it is quite clear that they must be explicitly set in mikroC.
So there. A nasty little trap which MikroElektronika does not state in their manual. Beware.
void OnLoad(unsigned char *salad) { *salad = 0xFF; } void OffLoad(unsigned char *greens) { *greens = 0; } void main() { while(1) { OnLoad(&PORTC); Delay_100ms(); OffLoad(&PORTC); Delay_100ms(); } }
The firware merely toggles PORTC on and off every tenth of a second. An LED is connected to one of the PORTC pins, so it should blink when the MCU is powered up. Well, it didn't work.
After a couple of hours of racking my brain and trying out variations on the program, I decided to test the code on a PIC 16F device. I inserted a PIC12F615 into the breadboard, plugged in an LED and resistor, and hooked up the PICKit 2. Opened a new project in mikroC and used the same program, changing PORTx to GPIO, and initializing the appropriate SFRs. Guess what? Worked the first time around. No problems. That told me there's nothing wrong with the program. Went back to the PIC18.
After several more hours of checking and hair-pulling, it occured to me that there might be something in the MCU configuration bits which hasn't been set properly. Referring to the datasheet, the only one that seemed relevant was the XINST (Extended Instruction Set enable) bit in configuration word CONFIG4L. Noting that according to the datashet it's off by default, I first enabled it. I compiled the firmware, uploaded to the MCU, and powered it up. Nothing. The program still wouldn't work. Went back to mikroC, unchecked the "XINST_ON_4L" box and checked "XINST_OFF_4L."
Hallelujah! The LED finally started blinking, and at the proper rate at that. Finally found the culprit! Just to make sure it was indeed the XINST bit, I enabled it again. Sure enough the program failed again. Disabling XINST once more and the firmware worked. As a final test, I unchecked both these boxes (it was in that condition before I fiddled around with the XINST) and uploaded the compiled code. It didn't work. Therefore, regardless of what Microchip says about the default condition of the configuration words, it is quite clear that they must be explicitly set in mikroC.
So there. A nasty little trap which MikroElektronika does not state in their manual. Beware.
Monday, September 20, 2010
Fluke face to face with Kryptonite
Australian electronics engineer Dave Jones proves that the Fluke 87V DMM goes haywire in the presence of GSM.
Much as I'd like to see this fault for myself, I simply cannot afford to fry my 87V. Instead I used a Fluke 117 as the lab rat. Apparently, the cheapo doesn't even know what a cell phone is. Barely a reaction and not a hint of malfunction.
This GSM susceptibility is yet another item in the list of disappointments I have with the 87V. First and foremost is its unforgivably flimsy 9V battery connector. It's no different from those found in China made toys!
I cannot imagine how Fluke could have blundered something as basic as this. My old Sanwa DMM had a far more superior battery-compartment-connector system. You unscrew the battery compartment at the back of the meter, remove it, flip it over and insert the battery into it, and then plug the whole thing back into the meter body and screw tight. Once inserted the battery terminals automatically make contact with metal prongs inside the meter. Absolutely great design! Now why couldn't Fluke have made something similar with this model? It's already the Series 5!
They certainly did it right on the Fluke 117:
Next on the list, and one that puts me off whenever I notice it is the LCD. For some reason Fluke seems to have sacrificed the viewing angle exactly perpendicular to the unit. When looking straight down the segments have rather poor contrast--not as black--as when looking at an angle. At first I thought my unit was faulty, but I've checked another brand new 87V and an 83V and their LCDs both have the same characteristic. And the batteries are still fresh so it's not a low voltage problem. I was never annoyed with the display of my old Sanwa so I guess it didn't have this quirk. Testing the 87V, the LCD is a 6:00 type with a bias angle somewhere between 20 and 30°. The readout is still tolerable even at 60°.
Much as I'd like to see this fault for myself, I simply cannot afford to fry my 87V. Instead I used a Fluke 117 as the lab rat. Apparently, the cheapo doesn't even know what a cell phone is. Barely a reaction and not a hint of malfunction.
This GSM susceptibility is yet another item in the list of disappointments I have with the 87V. First and foremost is its unforgivably flimsy 9V battery connector. It's no different from those found in China made toys!
I cannot imagine how Fluke could have blundered something as basic as this. My old Sanwa DMM had a far more superior battery-compartment-connector system. You unscrew the battery compartment at the back of the meter, remove it, flip it over and insert the battery into it, and then plug the whole thing back into the meter body and screw tight. Once inserted the battery terminals automatically make contact with metal prongs inside the meter. Absolutely great design! Now why couldn't Fluke have made something similar with this model? It's already the Series 5!
They certainly did it right on the Fluke 117:
Next on the list, and one that puts me off whenever I notice it is the LCD. For some reason Fluke seems to have sacrificed the viewing angle exactly perpendicular to the unit. When looking straight down the segments have rather poor contrast--not as black--as when looking at an angle. At first I thought my unit was faulty, but I've checked another brand new 87V and an 83V and their LCDs both have the same characteristic. And the batteries are still fresh so it's not a low voltage problem. I was never annoyed with the display of my old Sanwa so I guess it didn't have this quirk. Testing the 87V, the LCD is a 6:00 type with a bias angle somewhere between 20 and 30°. The readout is still tolerable even at 60°.
Sunday, September 12, 2010
Pantry light switch using a PIR module
Practically all the dry goods in our kitchen are stored in the pantry and we access this room several times a day. My bugbear has been that everyone who uses it, including myself, forgets to turn off the light rather frequently. Something about this tiny room that makes people run in and out less than a minute later and forget to flip the switch. Must the duration of stay inside. Or maybe not, since I also do fail to turn the garage storeroom lights off even if I've been in there for ten minutes. Yeah, must be the genes.
Been meaning to install a motion detector to address this problem but couldn't find a readily available PIR in the market. Went to a home depot months ago to buy a consumer security gadget that employed PIR and was going to just hack it. Problem is that when I asked them to test if it's working the dang model didn't. Not a single unit of those tried! What the bleep indeed.
Well, fortunately I found an online supplier who had reasonable prices. The module is China-made obviously, but right now I'll take anything over nothing. For details and specs of the passive infrared (PIR) module refer to my previous blog entry.
I could've used the module without employing a microcontroller. A transistor and a relay would've been sufficient to switch on the pantry's compact fluorescent lamp (CFL). But that's crude and not at all very user friendly. What happens when the person is still inside and isn't detected by the PIR? Darkness descends without warning. Therefore, there is a need to provide an alert of some sort that it'll be lights out in a while.
I would've wanted a visual warning, specifically a PWM controlled lamp that gradually dims. The decreasing light level would be a gentle way of informing the user. But such a circuit is more complicated and I certainly can't dim a CFL. LED high-lumen lighting would be nice, but cost is prohibitive and the drive circuitry complex.
So I settled for an audible alert, giving a graduated level of warning as the moment of lamp switch-off approaches. I arbitrarily set the start of warning five seconds before switch-off, with a 10ms beep every second for four seconds. On the last second, a 10ms beep is issued every 200ms for a total of five beeps. The beeps are generated by a small sonalert buzzer but at 10ms they're so short that they sound more like clicks than beeps. My design criterion on this is that the sound level and type should be informative but unobstrusive to those outside the room, so the clicks are on the money.
The MCU's job is simply to monitor the PIR module output and switch the CFL accordingly. When PIR output goes high the MCU immediately switches on the CFL, and when it goes low the MCU starts issuing the alert sequence, after which it turns off the lamp.
However, since this particular module has an initialization ritual it goes through upon power up, the MCU must also be programmed to disregard the output during this period. Well, the MCU doesn't really have to, but for now I've decided that it should ignore the two PIR output high during initialization since energy is wasted when the CFL on. On the other hand, there is some utility to providing feedback that the PIR is in initialization mode. This can be implemented using the buzzer. A distinct pattern of beeps can be continuously emitted during this period.
In anticipation of future PIR module malfunction and of those times when the user would want to keep the light on continuously--i.e., override the sensor--I designed the circuit with a switch to allow the user to turn off auto mode. When in manual mode the CFL will be on regardless of PIR output. The manual override is a surface mount rocker switch rated at 10A @250VAC. Only a feeble current (about half a milliamp DC) will be running through so it'll be dry switching. I'm not worried.
Since the MCU won't be doing much at all and will only need a small amount of code, I decided to use a baseline PIC, i.e., a model from the 10F series. The smallest (in Flash memory) chip I have in stock is the PIC10F202. It has four I/O pins which is exactly the number of pins I need.
Powering the CFL is via a miniature socketed relay with a 12VDC coil. The Telemecanique RXM2AB2JD is a DPDT switch with contacts rated at 12A @250VAC. As a bonus it has a green LED indicator which is on when the coil is energized. It even has a button which the user can push to momentarily close/open the NO/NC contacts and a mechanical lever to latch them. Love its smart look as well. Thumbs up to this model.
To serve as a troubleshooting aid I've added an LED which is switched on by a transistor whenever PIR output is high. The MOSFET is controlled directly by the PIR.
Powering the control circuitry is a generic wall cube which has no markings on it. Thankfully this unit isn't one of those whose casing is glued together. It has two screws which allows it to be opened up. As the schematic below shows it has a transformer, bridge rectifier made up of four diodes (can't make out the last digit of any of the 1N400x) and a filter capacitor. Output of the adapter is 19VDC, no load.
A LM7812 provides a regulated 12VDC. Although not strictly necessary given the current draw of the circuit, a clip-on anodized aluminum heat sink has been installed on this TO-220 as a precaution. To preclude having too high a voltage headroom, the 78L05 regulator's input is drawn from the output of the LM7812. This reduces the dissipation of the 5VDC regulator.
Here are the schematic diagrams and legend to the reference designations.
The 10F series of PICs has no interrupt capability and only a 2 level stack for subroutine calls. Pretty spartan indeed, but quite adequate for simple undemanding circuit requirements like this one. I've used the 10F202 for a number of circuits but something I've not learned or already forgotten manage to trip me up. Pretty vexing really. While prototyping I used GP2 as the I/O for the sonalert buzzer. Well, it didn't work! I went over the firmware and it looked good. Took me a long time poring over the datasheet for me to finally discover that GP2 is by default an input pin even if the TRISGPIO2 bit has been correctly set as output. The fact is the TRISIO register has the lowest priority in determining the status of this pin. OSCCAL register FOSC4 bit has the highest priority followed by OPTION register TOCS bit. No problem with FOSC4 since it is upon power up set to zero which means it hands off this pin to other functions. The problem lies with TOCS. It's set to 1 upon any type of reset including power on. And that means the pin is in high impedance mode waiting for a signal. With TOCS set, Timer0 is in counter mode and increments upon a level change (edge trigger) on the GP2 pin. So in order to use this pin as a general I/O TOCS has to be cleared. The 10F202 is practically the most basic MCU in Microchip's line but I have yet to master it.
In the end I moved the buzzer to GP1 and used GP2 as an input to sense the status of the auto/manual switch. GP3 is input-only and is used to monitor PIR output. Note that the PIR module works at 3.3VDC and its output high can only reach a maximum of this value. With a VDD of 5V a Schmitt trigger input on the MCU won't be able to detect the PIR high since the minimum requirement for a level high is 80% of VDD, or 4V. Fortunately, when the 10F202 pins are configured as general purpose inputs they are all TTL which has a minimum requirement of only 2.0V to be considered as high. Not so in other cases. For instance when TOCS bit = 1, GP2 becomes a Schmitt trigger input for Timer0 counter. Thus, if we feed the PIR output to this pin, Timer0 would never increment. What the PIR outputs as high would be considered a valid high only if MCU VDD drops to 4.1VDC or less.
Firmware consists mainly of a series of polling loops. PIR output is continually monitored while the manual switch is monitored continually only after PIR initialization is complete. When switch SW1 is sensed to be in manual and as long as it is in manual position, PIR is no longer monitored. When SW1 is flipped back to auto position then PIR monitoring resumes.
In auto mode and after PIR initialization has been detected to be over, when PIR output goes high, Q2 is immediately turned on which then switches REL on and hence LOAD. When PIR output goes low, the pattern of beeps (as described above) is issued. Five seconds later LOAD is switched off. If PIR output goes high during this five-second waiting time, the alerts stop and firmware exits the alert routine.
When user switches from manual to auto mode, firmware checks PIR output to determine whether LOAD will be turned off or not. PIR will almost certainly be high since SW1 is located inside the pantry and in an area right under the PIR, thus making sure the user will be detected while operating the switch. Be that as it may, if PIR output is low then LOAD is turned off, otherwise it is kept on.
Currently, there is no routine to issue an audible alert informing the user that the PIR is initializing. There should be. That will probably come in a firmware upgrade.
The actual firmware installed is provided below. It's written in MPLAB assembly. Uses only 87 words of flash and 4 bytes of RAM. Watch out! I use homebrew macros and defines throughout. Blogspot truncates any text that extends beyond the margins so if you want to read the code and comments I suggest copying and pasting into a word processor. Or paste it in MPLAB, copy and paste the macros and defines from the above link as well, build it, and read the disassembly listing or "program memory" listing if you're not a huge fan of my macros. Shame on you if you aren't!
The Installation
I need to have control over the area covered/monitored by the PIR sensor but don't have the resources nor the perseverance to build a metal articulated aiming gizmo. Scrounged around and found a round tea can made of cardboard whose slip-on lid could be rotated while it was on. That provides one axis of rotation to aim the the sensor. I could easily bore a hole on the side of the canister and fasten the module with the Fresnel lens sticking out. The lid would then be screwed onto a block of wood which in turn would be bolted to the side panel of the top shelf. If I use just one bolt I can swivel the block. That's the second axis of rotation which would be at right angles to the first. Two planes of swivel is sufficient to aim the sensor practically anywhere.
Here are photos of the installation. The first shows the can with the module already in place. I wish I didn't have to glue it to the can but my brain was out of options. The can is actually red. I covered with white paper since the shelves are all white. I don't want it out more than it already will be and calling attention to it. The three wires lead to the circuit board where all the other components are.
The lid of the canister is shown fastened to a scrap piece of wood which is epoxied (the black goo) to another piece perpendicular to the former. The latter is bolted to the shelf side panel. The outlet on the pantry wall is newly installed just for this circuit. The thicker SPT cable is 220VAC power line and the smaller gauge SPT leads from the relay to the CFL. The translucent plastic box contains the circuit board and relay
In the bottom picture, the circuit is powered and operational. There's the wall wart plugged in with its (newly installed) red and black speaker cable leading to the circuit board inside the plastic box. The wire from the black plug leads to the relay. The white SPT wire along the bottom of the photo leads to the auto-manual switch located several shelves below. You can see I just taped it along the edge of the shelf.
In this last photo I am at ground level pointing the camera up. CFL is mounted on the ceiling less than a meter from the module.
Been meaning to install a motion detector to address this problem but couldn't find a readily available PIR in the market. Went to a home depot months ago to buy a consumer security gadget that employed PIR and was going to just hack it. Problem is that when I asked them to test if it's working the dang model didn't. Not a single unit of those tried! What the bleep indeed.
Well, fortunately I found an online supplier who had reasonable prices. The module is China-made obviously, but right now I'll take anything over nothing. For details and specs of the passive infrared (PIR) module refer to my previous blog entry.
I could've used the module without employing a microcontroller. A transistor and a relay would've been sufficient to switch on the pantry's compact fluorescent lamp (CFL). But that's crude and not at all very user friendly. What happens when the person is still inside and isn't detected by the PIR? Darkness descends without warning. Therefore, there is a need to provide an alert of some sort that it'll be lights out in a while.
I would've wanted a visual warning, specifically a PWM controlled lamp that gradually dims. The decreasing light level would be a gentle way of informing the user. But such a circuit is more complicated and I certainly can't dim a CFL. LED high-lumen lighting would be nice, but cost is prohibitive and the drive circuitry complex.
So I settled for an audible alert, giving a graduated level of warning as the moment of lamp switch-off approaches. I arbitrarily set the start of warning five seconds before switch-off, with a 10ms beep every second for four seconds. On the last second, a 10ms beep is issued every 200ms for a total of five beeps. The beeps are generated by a small sonalert buzzer but at 10ms they're so short that they sound more like clicks than beeps. My design criterion on this is that the sound level and type should be informative but unobstrusive to those outside the room, so the clicks are on the money.
The MCU's job is simply to monitor the PIR module output and switch the CFL accordingly. When PIR output goes high the MCU immediately switches on the CFL, and when it goes low the MCU starts issuing the alert sequence, after which it turns off the lamp.
However, since this particular module has an initialization ritual it goes through upon power up, the MCU must also be programmed to disregard the output during this period. Well, the MCU doesn't really have to, but for now I've decided that it should ignore the two PIR output high during initialization since energy is wasted when the CFL on. On the other hand, there is some utility to providing feedback that the PIR is in initialization mode. This can be implemented using the buzzer. A distinct pattern of beeps can be continuously emitted during this period.
In anticipation of future PIR module malfunction and of those times when the user would want to keep the light on continuously--i.e., override the sensor--I designed the circuit with a switch to allow the user to turn off auto mode. When in manual mode the CFL will be on regardless of PIR output. The manual override is a surface mount rocker switch rated at 10A @250VAC. Only a feeble current (about half a milliamp DC) will be running through so it'll be dry switching. I'm not worried.
Since the MCU won't be doing much at all and will only need a small amount of code, I decided to use a baseline PIC, i.e., a model from the 10F series. The smallest (in Flash memory) chip I have in stock is the PIC10F202. It has four I/O pins which is exactly the number of pins I need.
Powering the CFL is via a miniature socketed relay with a 12VDC coil. The Telemecanique RXM2AB2JD is a DPDT switch with contacts rated at 12A @250VAC. As a bonus it has a green LED indicator which is on when the coil is energized. It even has a button which the user can push to momentarily close/open the NO/NC contacts and a mechanical lever to latch them. Love its smart look as well. Thumbs up to this model.
To serve as a troubleshooting aid I've added an LED which is switched on by a transistor whenever PIR output is high. The MOSFET is controlled directly by the PIR.
Powering the control circuitry is a generic wall cube which has no markings on it. Thankfully this unit isn't one of those whose casing is glued together. It has two screws which allows it to be opened up. As the schematic below shows it has a transformer, bridge rectifier made up of four diodes (can't make out the last digit of any of the 1N400x) and a filter capacitor. Output of the adapter is 19VDC, no load.
A LM7812 provides a regulated 12VDC. Although not strictly necessary given the current draw of the circuit, a clip-on anodized aluminum heat sink has been installed on this TO-220 as a precaution. To preclude having too high a voltage headroom, the 78L05 regulator's input is drawn from the output of the LM7812. This reduces the dissipation of the 5VDC regulator.
Here are the schematic diagrams and legend to the reference designations.
The 10F series of PICs has no interrupt capability and only a 2 level stack for subroutine calls. Pretty spartan indeed, but quite adequate for simple undemanding circuit requirements like this one. I've used the 10F202 for a number of circuits but something I've not learned or already forgotten manage to trip me up. Pretty vexing really. While prototyping I used GP2 as the I/O for the sonalert buzzer. Well, it didn't work! I went over the firmware and it looked good. Took me a long time poring over the datasheet for me to finally discover that GP2 is by default an input pin even if the TRISGPIO2 bit has been correctly set as output. The fact is the TRISIO register has the lowest priority in determining the status of this pin. OSCCAL register FOSC4 bit has the highest priority followed by OPTION register TOCS bit. No problem with FOSC4 since it is upon power up set to zero which means it hands off this pin to other functions. The problem lies with TOCS. It's set to 1 upon any type of reset including power on. And that means the pin is in high impedance mode waiting for a signal. With TOCS set, Timer0 is in counter mode and increments upon a level change (edge trigger) on the GP2 pin. So in order to use this pin as a general I/O TOCS has to be cleared. The 10F202 is practically the most basic MCU in Microchip's line but I have yet to master it.
In the end I moved the buzzer to GP1 and used GP2 as an input to sense the status of the auto/manual switch. GP3 is input-only and is used to monitor PIR output. Note that the PIR module works at 3.3VDC and its output high can only reach a maximum of this value. With a VDD of 5V a Schmitt trigger input on the MCU won't be able to detect the PIR high since the minimum requirement for a level high is 80% of VDD, or 4V. Fortunately, when the 10F202 pins are configured as general purpose inputs they are all TTL which has a minimum requirement of only 2.0V to be considered as high. Not so in other cases. For instance when TOCS bit = 1, GP2 becomes a Schmitt trigger input for Timer0 counter. Thus, if we feed the PIR output to this pin, Timer0 would never increment. What the PIR outputs as high would be considered a valid high only if MCU VDD drops to 4.1VDC or less.
Firmware consists mainly of a series of polling loops. PIR output is continually monitored while the manual switch is monitored continually only after PIR initialization is complete. When switch SW1 is sensed to be in manual and as long as it is in manual position, PIR is no longer monitored. When SW1 is flipped back to auto position then PIR monitoring resumes.
In auto mode and after PIR initialization has been detected to be over, when PIR output goes high, Q2 is immediately turned on which then switches REL on and hence LOAD. When PIR output goes low, the pattern of beeps (as described above) is issued. Five seconds later LOAD is switched off. If PIR output goes high during this five-second waiting time, the alerts stop and firmware exits the alert routine.
When user switches from manual to auto mode, firmware checks PIR output to determine whether LOAD will be turned off or not. PIR will almost certainly be high since SW1 is located inside the pantry and in an area right under the PIR, thus making sure the user will be detected while operating the switch. Be that as it may, if PIR output is low then LOAD is turned off, otherwise it is kept on.
Currently, there is no routine to issue an audible alert informing the user that the PIR is initializing. There should be. That will probably come in a firmware upgrade.
The actual firmware installed is provided below. It's written in MPLAB assembly. Uses only 87 words of flash and 4 bytes of RAM. Watch out! I use homebrew macros and defines throughout. Blogspot truncates any text that extends beyond the margins so if you want to read the code and comments I suggest copying and pasting into a word processor. Or paste it in MPLAB, copy and paste the macros and defines from the above link as well, build it, and read the disassembly listing or "program memory" listing if you're not a huge fan of my macros. Shame on you if you aren't!
; ******************************************** ; Passive Infrared Motion Detector for home panty ; September 2010 ; ; see "PIR sensor.dwg" for schematic ; ********************************************* processor 10F202 #include p10F202.inc #include et.inc __config _MCLRE_OFF & _CP_OFF & _WDT_ON cblock 0x08 CENTISECS ; number of centiseconds for time delay ICOUNTER ; counter for iterations COUNTER:2 ; counters for delays endc #define buz GPIO,0 ; sonalert buzzer #define load GPIO,1 ; switches MOSFET which switches 12VDC relay which switches 220VAC CFL #define sw GPIO,2 ; mode switch: high = manual (load is continuously on), low = auto (motion detect) #define pir GPIO,3 ; PIR output, high = motion detected, low = no motion detected org 0x0 movwf OSCCAL ; load clock calibration goto initialize ; ======================================================================================= ; subroutines ; ; 10F202 can only call subroutines if they are located in first 256 memory locations ; ======================================================================================= ; --------------------------------------------------------------------------------- ; Time delay that's user dependent ; Place number of centiseconds of delay in register CENTISECS before calling this routine DelayCentisec: movlf .164, COUNTER movlf .20, COUNTER+1 delay_loop decfsz COUNTER,f goto delay_loop movlf .164, COUNTER decfsz COUNTER+1,f goto delay_loop clrwdt movlf .20, COUNTER+1 decfsz CENTISECS,f goto delay_loop retlw 0 ; --------------------------------------------------------------------------------- ; --------------------------------------------------------------------------------- ; one centisec beep Beep: bsf buz movlf .1, CENTISECS call DelayCentisec bcf buz retlw 0 ; --------------------------------------------------------------------------------- ; ======================================================================================= ; main program ; ======================================================================================= ; --------------------------------------------------------------------------------- initialize: clrf GPIO movlw b'11011111' ; TOCS bit must be 0 else GP2 will be an input pin; prescaler is assigned to WDT; prescale = 1:2 option movlw b'1100' ; GPIO 0 and 1 are assigned as output, GP2 is input, GP3 is by default an input only pin tris GPIO ; do nothing for about half a second (50 centisec) to give PIR time to initialize and bring its output high movlf .50, CENTISECS call DelayCentisec ; PIR initializes by outputting high twice over a minute or two ; wait until the second falling edge is detected; thereafter any edges detected will be due to normal operation of motion detection ; wait for first PIR falling edge init_pir_loop1 clrwdt dnxtrue pir goto init_pir_loop1 ; wait for PIR rising edge init_pir_loop2 clrwdt dnxfalse pir goto init_pir_loop2 ; wait for second PIR falling edge--signals the end of PIR initialization init_pir_loop3 clrwdt dnxtrue pir goto init_pir_loop3 ; --------------------------------------------------------------------------------- ; --------------------------------------------------------------------------------- main: ; wait for PIR rising edge--which signals that motion has been detected ; check mode sw status clrwdt dnxtrue sw goto manual dnxfalse pir goto main main_motion_true bsf load ; PIR output is high so turn load on main_loop1 ; wait until no motion is detected--until PIR output goes low ; check mode sw status clrwdt dnxtrue sw goto manual dnxtrue pir goto main_loop1 ; PIR output is now low ; give user 5 seconds to activate motion sensor before turning off load ; begin issuing 10ms beep every second for four seconds to alert user that motion is no longer detected ; check PIR output every second; if it goes high (motion is detected) then exit and go to main_loop1 ; check mode sw status every second; if sw level goes high then go to manual mode routine main_motion_false movlf .4, ICOUNTER main_beep_loop0 call Beep movlf .99, CENTISECS call DelayCentisec dnxtrue sw goto manual dnxtrue pir goto main_loop1 decfsz ICOUNTER,f goto main_beep_loop0 ; for the last second issue a 10ms-beep every 200ms ; check PIR output every second; if it goes high (motion is detected) then exit and go to main_loop1 ; check mode sw status every second; if sw level goes high then go to manual mode routine movlf .5, ICOUNTER main_lastsec_beep_loop0 call Beep movlf .19, CENTISECS call DelayCentisec dnxtrue sw goto manual dnxtrue pir goto main_loop1 decfsz ICOUNTER,f goto main_lastsec_beep_loop0 ; if PIR output remains low then turn load off ; go to main whether PIR output is low or high dnxfalse pir bcf load goto main ; manual mode routine ; if sw level is high then turn load on ; if sw level is low and PIR output is low then turn off load ; if sw level is low and PIR output is high then keep load on ; when sw level is low then go to main manual: bsf load manual_loop1 dnxfalse sw goto manual_loop2 clrwdt goto manual_loop1 manual_loop2 dnxfalse pir bcf load goto main end ; ----------------------------------------------------------------------------------
The Installation
I need to have control over the area covered/monitored by the PIR sensor but don't have the resources nor the perseverance to build a metal articulated aiming gizmo. Scrounged around and found a round tea can made of cardboard whose slip-on lid could be rotated while it was on. That provides one axis of rotation to aim the the sensor. I could easily bore a hole on the side of the canister and fasten the module with the Fresnel lens sticking out. The lid would then be screwed onto a block of wood which in turn would be bolted to the side panel of the top shelf. If I use just one bolt I can swivel the block. That's the second axis of rotation which would be at right angles to the first. Two planes of swivel is sufficient to aim the sensor practically anywhere.
Here are photos of the installation. The first shows the can with the module already in place. I wish I didn't have to glue it to the can but my brain was out of options. The can is actually red. I covered with white paper since the shelves are all white. I don't want it out more than it already will be and calling attention to it. The three wires lead to the circuit board where all the other components are.
The lid of the canister is shown fastened to a scrap piece of wood which is epoxied (the black goo) to another piece perpendicular to the former. The latter is bolted to the shelf side panel. The outlet on the pantry wall is newly installed just for this circuit. The thicker SPT cable is 220VAC power line and the smaller gauge SPT leads from the relay to the CFL. The translucent plastic box contains the circuit board and relay
In the bottom picture, the circuit is powered and operational. There's the wall wart plugged in with its (newly installed) red and black speaker cable leading to the circuit board inside the plastic box. The wire from the black plug leads to the relay. The white SPT wire along the bottom of the photo leads to the auto-manual switch located several shelves below. You can see I just taped it along the edge of the shelf.
In this last photo I am at ground level pointing the camera up. CFL is mounted on the ceiling less than a meter from the module.
Subscribe to:
Posts (Atom)