My first PIC program - Page 1
Forum section: electronics / projects

author: Zero999
author_rank: Super Contributor
timestamp: September 16, 2010, 09:17:03 pm
content: I've been wanting to get into MCUs for awhile, I've had a PICkit 2 clone programmer for a few weeks now and have only just got round to reading a tutorial and writing a program.Here's the schematic and code. The idea is to control an extractor fan which will be connect to a pull switch. Once the cord is pulled, the fan will turn on until ten minutes has passed or the cord is pulled again. For the purposes of testing the delay has been shortened to 10 seconds and the relay has been replaced with an LED.Code:;************************************************************************; *; Description: Extractor Fan Timer *; *; Waits for the switch to be activated then turns on the fan which *; stays on until either a period of time has passed or the switch *; is pulled again. Switch debouncing is performed where requireed. *; *; *;************************************************************************; *; Pin assignments: *; GP1 - Fan output *; GP3 - Pullcord switch *; *;************************************************************************list p=12F509#include <p12F509.inc>; int reset, no code protect, no watchdog, 4MHz int clock__CONFIG _MCLRE_OFF & _CP_OFF & _WDT_OFF & _IntRC_OSC;***** VARIABLE DEFINITIONSUDATAsGPIO res 1; shadow copy of GPIOdc1 res 1; delay counter 1dc2res 1; delay counter 2;************************************************************************RESET CODE 0x000; effective reset vectormovwf OSCCAL; update OSCCAL with factory cal valuepagesel startgotostart; jump to main code;***** Subroutine vectorswait_up; wait for switch to be releasedpagesel wait_up_Rgoto wait_up_R;***** MAIN PROGRAMMAINCODE;***** Initialisationstartmovlw b'111101'; configure GP1 (only) as an outputtris GPIOmovlw b'10010101'; enable internal pull-ups & configure Timer0:; -0------pullups enabled (/GPPU = 0); --0-----timer mode (T0CS = 0); ----0---prescaler assigned to Timer0 (PSA = 0); -----101prescale = 64 (PS = 101)option; -> increment every 64 usclrf GPIO; start with fan offclrf sGPIO; update shadow;***** Main looploop; wait until switch pulled, debounce using timer0:wait_dn clrf TMR0; reset timerchk_dn btfsc GPIO,3; check for switch pull (GP3 low)goto wait_dn; continue to reset timer until button downmovf TMR0,w; has 10ms debounce time elapsed?xorlw .156; (156=10ms/64us)btfss STATUS,Z; if not, continue checking buttongoto chk_dn; turn on fan on GP1bsf sGPIO,1; set bit 1 on shadow registermovf sGPIO,w; write back to shadow registermovwf GPIO; write to portcallwait_up; wait until switch is released; waits until either a the switch is pulled or the time delay is overmovlw.4; delay is in multiples of 2.5 secondsmovwfdc1delay_loopmovlw.250; waits 2.5s = 250*10ms, switch scanned every 10msmovwfdc2delay2s; waits 10msclrfTMR0; reset timerdelay10movfTMR0,w; has 10ms elapsed?xorlwd'156'; (156=10ms/64us)btfssSTATUS,Z; if not, go back to the beginninggotodelay10btfssGPIO,3; check for switch pull (GP3 low)gototurn_off; if switch pulled, exit delay and turn off fandecfszdc2,f; decrement counter, skip goto, if zerogoto delay2s; if counter is non-zero loopdecfszdc1,f; decrement counter, skip goto, if zero.gotodelay_loopturn_off; turn off fan on GP1clrfGPIO; fan offclrfsGPIO; update shadowcallwait_up; wait until switch is released, if pressedgotoloop; repeat forever;***** SubroutinesSUBS CODE; wait until switch released, debounce using timer0:wait_up_RclrfTMR0; reset timerchk_upbtfssGPIO,3; check for button release (GP3 high)gotowait_up_R; continue to reset timer until button upmovfTMR0,w; has 10ms debounce time elapsed?xorlwd'156'; (156=10ms/64us = 10/0.064)btfssSTATUS,Z; if not, continue checking buttongotochk_upretlw0ENDI'll modify the code so if the switch is quickly pulled twice, it'll stay on continuously.EDIT:Here's a lnk to the tutorials I've followed, as you can see, I've borrowed lots of the code from demonstrations.http://www.gooligum.com.au/tutorials/baseline/PIC_Base_A_4.pdfYes I know I could use some constants to make my code easier to read and I could've used timer debouncing in the main loop but I didn't think it was worth it and my tests indicate that it isn't necessary, just reading the switch status every 10ms seems to work fine.

author: scrat
author_rank: Frequent Contributor
timestamp: September 17, 2010, 12:11:07 am
content: You used assembly... Was it hard at beginning? Did you consider starting with C or was assembly your choice just to learn more in deep?edit: however, it's not a simple "hello world", the program is quite complex to start with. Congrats!

author: ArtemisGoldfish
author_rank: Regular Contributor
timestamp: September 17, 2010, 01:31:14 am
content: Assembly can be significantly easier than high-level languages for simple tasks, though for some things, I'd rather leave the impossible stuff to the compiler of a high-level language.

author: Mechatrommer
author_rank: Super Contributor
timestamp: September 17, 2010, 01:38:32 am
content: most people dont like asm for mcu i think. except me and some of us, but even me struggling to understand my own code after i leaved it for a while, worst if its somebody else's code. all i need is a fresh big cup of cafein, a long stick of nicotine and a cold deep nightsorry i cannot finish reading your code, but if its working, then its working. Cheers!ps: Agreed with Artemish though!

author: Simon
author_rank: Unknown
timestamp: September 17, 2010, 05:59:52 am
content: well I have to say that as sinple as programming my DC-AC driver MCU was I prefered to do it in Basic as that's all i know and it was still a doddle, just a couple of registers to setup as I'd have to do in assembly and then about 10 lines of code, it would have been no more simple in assembly

author: Zero999
author_rank: Super Contributor
timestamp: September 17, 2010, 08:43:28 am
content: Quote from: scrat on September 17, 2010, 12:11:07 amI know both BASIC and some assembler for the PC. I tried C but actually found it harder than assembler so didn't bother learning it.I've only used assembly for MCUs and it's not hard but it did take a few complies before my code worked:The first time the code didn't compile because MPASM doesn't like decimal points, I tried 10/0.64 which has to be represented as d'10'/d'0.064" which it didn't like, then I tried .10/.0.064 which didn't work either so I just did 156.The second time it compiled but it didn't work because I made a mistake with the code to turn on the fan.The third time there was a mistake in the delay code.Finally it worked, I don't think the third attempt is too bad considering I've never written a PIC program before.Quote from: Simon on September 17, 2010, 05:59:52 amThe program is much more complicated than it needs to be but the main reason for assembly is to learn something. I could probably pick up BASIC in 5 minutes but you never learn much doing things the easy way!Is there a switch debouncing library in BASIC? Does it get done automatically? If so, it's something you'll never learn how to do and you'll probably never even learn about the problem of switch bounce. The problem is, when a switch is pressed or released it bounces on and off for a couple of ms afterwards generating a stream of pulses which are accepted by the MCU so your program needs to ignore them.In my program debouncing is implemented using the timer: the switch has to be pressed or released for at least 10ms, before the program considers it to be in the new state.For example, to wait of the switch to be activated the BASIC the code would be something like:Code:' switch on GP3 which is low when activated.' Set up timer and GP3, I don't know how this is done in basicset timer to TOS ' timer counts clock pulsesset timer prescaler 64 ' set prescaler so timer counts every 64µsset GP3 as inputdodoTMR0 = 0 ' reset timerloop while GP3 ' loop while switch is not pressed, GP3 is highloop until TMR0 = 10/0.064 ' loop for 10msThe assembly code is a little longer (mostly due to my comments) but it's not that bad, it's only more difficult to understand because you need to know what each instruction actually does and what the registers are.Code:;***** Initialisationstartmovlw b'111101'; configure GP1 (only) as an outputtris GPIOmovlw b'10010101'; enable internal pull-ups & configure Timer0:; -0------pullups enabled (/GPPU = 0); --0-----timer mode (T0CS = 0); ----0---prescaler assigned to Timer0 (PSA = 0); -----101prescale = 64 (PS = 101)option; -> increment every 64 usclrf GPIO; start with fan offclrf sGPIO; update shadow; wait until switch pulled, debounce using timer0:wait_dn clrf TMR0; reset timerchk_dn btfsc GPIO,3; check for switch pull (GP3 low)goto wait_dn; continue to reset timer until button downmovf TMR0,w; has 10ms debounce time elapsed?xorlw .156; (156=10ms/64us)btfss STATUS,Z; if not, continue checking buttongoto chk_dnAnother way of doing it is to check for the switch to be activated and simply wait 10ms until checking it again which is not quite so robust as a pulse of EMI might trigger it. My program also uses this method during the timing delay which also uses the timer - there's only one timer on the 12F509 and it can't be used for two different things simultaneously.There are limitations to the 12F509 architecture which make the code longer and harder to understand, for example because addresses are limited to 9 bits, the 1024 words of program memory is split into two pages so to do a long jump the correct page has to be selected first e.g.Code:pageselstartgotostart; jump to main codeThe call instruction is limited to 8 bits so all subroutines need to appear at the start of a memory page i.e. the first 256 words so you need to do things like:Code:;************************************************************************RESET CODE 0x000; effective reset vectormovwf OSCCAL; update OSCCAL with factory cal valuepagesel startgotostart; jump to main code;***** Subroutine vectorssubroutine; wait for switch to be releasedpagesel subroutine_Rgoto subroutine_R;***** MAIN PROGRAMMAINCODE;***** Initialisationstart; your program code herecall subroutinesubroutine_R; the code for the subroutine goes hereretlwsubroutine is called, which just selects the correct page and jumps to subroutine_R which is where the real code is.Although I didn't need to worry about any of this, my program is under 256 words, I still did all of that as it's good practise and my program would break if it exceeded 256 words.If you're using BASIC or C, this is all done for you so you never really learn how the MCU works.

author: Simon
author_rank: Unknown
timestamp: September 17, 2010, 12:31:51 pm
content: I think there is a switch debounce in mikroe basic but really you don't need it, you looking at it the wrong way: the MCU will not normally hang about for 10mS to see if the switch is pressed, if the program is a loop like most are in the time you press the switch it probably runs at least once so a fail contact is not an issue and once hooked it gets on with it's thing without looking at the switch until told to again. I'm sure there are som instances where you need a proper switch debounce and even manually easily done in basic. Basic still requires much registry setup so you still need to know about the pic setup it just means you can write you code faster and it's easier to review months or years later as what it does is often quite obvious

author: Zero999
author_rank: Super Contributor
timestamp: September 17, 2010, 01:09:40 pm
content: This is an example where debouncing is required, any program which is dependant on a switch changing state more than once will require some form of debouncing.Let's look at the basic program function again.1 Wait for the switch to be pressed.2 Wait until the switch is released.3 Turn on fan.4 Wait for 10 minutes or until the switch is pressed again.5 Turn off fan6 Wait for switch to be released.7 Go back to 1Suppose the switch generates two pulses with a 1ms gap between each other when it's pressed once, how will it effect the program?The fan will quickly turn on and off before the motor has time to start. In reality the switch will probably bounce more than once so the fan will turn on and off until the switch finishes bouncing: the motor might turn on or it could remain off. The same could happen when the fan is on: it might turn off or it could remain on.It you don't believe me, write a simple program to simulate an on/off switch, when the push button is pressed, the LED turns on, when it's pressed again, the LED turns off. You'll find that the LED won't always turn on or turn off when the switch is pressed. Add a 10ms delay somewhere in the loop and it will be much better but could still be effected by EMI, add a counter routine and it'll be perfect. You'll have the same problem with a divide by 2 counter built with the CD4013, the problem of switch bounce has existed long before MCUs, it probably first arose with the first state machines constructed from valves, which were fast enough to respond to switch bounce.

author: jahonen
author_rank: Super Contributor
timestamp: September 17, 2010, 02:17:51 pm
content: Assembly is superficially simple for simple programs (like shown here), but C advantage quickly becomes clear when one does something more complicated applications (something which requires larger than 10-16F PIC). One can try writing something like serial terminal command line parser (have done that, string compare is just a pain in PIC asm, or just accessing string constants) or high-level protocol stack (TCP/IP, USB device classes etc.) with assembler. Not very clear anymore.Regards,Janne

author: Zero999
author_rank: Super Contributor
timestamp: September 19, 2010, 05:16:40 pm
content: I hated the way C handles strings: BASIC is much easier to work with.The x86 has instructions targeted specifically at handling strings but I can see it being a nightmare with PICs and almost impossible with baseline PICs so you're probably right.Anyway, here's the latest version of the program. I've changed it to remain on contentiously if the switch is pulled again 500ms after it's first been pulled.The operating is as follows:Pull switch once, fan is on until either delay expires or it's pulled again.Pull switch twice within 500ms, fan is on until it's pulled again.I'm embarrassed to say, t actually took me awhile to get this working. What I hadn't realised is that the retlw instruction doesn't update the zero flag so the btfss instruction following the call wasn't having any effect. What was worse was that the program was working as it was before so it appeared that the switch being pulled whilst delay_sw was working. What was happening was that the switch was being held down for at least 40ms each time so the delay_sw was executing in 10ms each time causing the delay to expire instantly.This was fixed by adding iorlw 0 after each call but I decided it would be better to add bsf or bsc STATUS,Z before the retlw instruction so the status register is modified by the delay_sw routine.Code:;********************************************************************************;*;Description:Extractor Fan Timer*;*;Waits for the switch to be activated then turns on the fan which*;stays on until either a period of time has passed or the switch*;is pulled again. If the switch is pulled again within 500ms, the*;fan stays on continiously, until the switch is pulled again.*;Switch debouncing is performed where requireed.*;*;********************************************************************************;*;Pin assignments:*;GP1 - Fan output*;GP3 - Pullcord switch*;*;********************************************************************************listp=12F509include<p12F509.inc>; int reset, no code protect, no watchdog, 4MHz int clock__CONFIG _MCLRE_OFF & _CP_OFF & _WDT_OFF & _IntRC_OSC;***** VARIABLE DEFINITIONSUDATAsGPIO res1; shadow copy of GPIOdc1 res1; delay counter 1dc2res1; delay counter 2;************************************************************************RESETCODE0x000; effective reset vectormovwfOSCCAL; update OSCCAL with factory cal valuepagesel startgotostart; jump to main code;***** Subroutine vectorswait_up; wait for switch to be releasedpageselwait_up_Rgotowait_up_Rwait_dn; wait for switch to be pressedpageselwait_dn_Rgotowait_dn_Rdelay_sw; wait for switch press or delay to expirepageseldelay_sw_Rgotodelay_sw_R;***** MAIN PROGRAMMAINCODE;***** Initialisationstartmovlwb'111101'; configure GP1 (only) as an outputtrisGPIOmovlwb'10010101'; enable internal pull-ups & configure Timer0:; -0------pullups enabled (/GPPU = 0); --0-----timer mode (T0CS = 0); ----0---prescaler assigned to Timer0 (PSA = 0); -----101prescale = 64 (PS = 101)option; -> increment every 64 usclrfGPIO; start with fan offclrfsGPIO; update shadow;***** Main looploop; wait until switch pulled, debounce using timer0:callwait_dn; turn on fan on GP1bsfsGPIO,1; set bit 1 on shadow registermovfsGPIO,w; write back to shadow registermovwfGPIO; write to portcallwait_up; wait until switch is releasedmovlwd'50'; delay in multiples of 10ms, 500ms = 50*10mscalldelay_sw; is the switch pulled again within the next 500ms?btfscSTATUS,Z; skip next goto if switch pressed (non-zero)gotomain_delay; if switch not pressed goto main delaycallwait_up; otherwise wait until switch is releasedcallwait_dn; wait until switch is pressed againgototurn_off; when switch is pressed again turn off fanmain_delay; long delay: waits until the switch is pulled or the time delay is overmovlwd'4'; delay is in multiples of 2.5 secondsmovwfdc1delay_loopmovlwd'250'; waits 2.5s = 250*10ms, switch scanned every 10mscalldelay_sw;btfssSTATUS,Z; skip next goto if switch not pressed (non-zero)gototurn_off; if non-zero turn fan offdecfszdc1,f; decrement counter, skip goto, if zerogotodelay_loopturn_off; turn off fan on GP1clrfGPIO; fan offclrfsGPIO; update shadowcallwait_up; wait until switch is released, if pressedgotoloop; repeat forever;***** SubroutinesSUBS CODE; wait until switch released, debounce using timer0:wait_up_RclrfTMR0; reset timerchk_upbtfssGPIO,3; check for button release (GP3 high)gotowait_up_R; continue to reset timer until button upmovfTMR0,w; has 10ms debounce time elapsed?xorlwd'156'; (156=10ms/64us = 10/0.064)btfssSTATUS,Z; if not, continue checking buttongotochk_upretlw0; wait until switch pulled, debounce using timer0:wait_dn_RclrfTMR0; reset timerchk_dn btfscGPIO,3; check for switch pull (GP3 low)gotowait_dn; continue to reset timer until button downmovfTMR0,w; has 10ms debounce time elapsed?xorlwd'156';(156=10ms/64us)btfssSTATUS,Z; if not, continue checking buttongotochk_dnretlw0; wait until switch is pressed or time delay has expired; delay is in multiples of 10ms and is stored in the working register; -1 is returned if switch (scanned every 10ms) is pressed otherwise 0; the zero flag in the status register is set or cleared accordinglydelay_sw_Rmovwfdc2delayx; waits 10msclrfTMR0; reset timerdelay10movfTMR0,w; has 10ms elapsed?xorlwd'156'; (156=10ms/64us)btfssSTATUS,Z; if not, go back to the beginninggotodelay10bcfSTATUS,ZbtfssGPIO,3; check for switch pull (GP3 low)retlw-1; if switch pulled, exit delay and pass a -1decfszdc2,f; decrement counter, skip goto, if zerogotodelayx; if counter is non-zero loopbsfSTATUS,Zretlw0; if timer has expired, return zeroEND

author: Thermal Runaway
author_rank: Contributor
timestamp: September 20, 2010, 01:47:27 pm
content: Congratulations on your first PIC program! My first program was literally just to light an LED!!!I learned to program PICs in assembly originally. Prior to that I'd done some stuff with 6502, Z80 and 68000 processors in assembly.These days I rarely find an application for assembly to be honest. I almost exclusively use C as my programming language of choice. You mentioned that you didn't think it was worth learning C - I would recommend that you reconsider this if you intend to write larger more complex programs in the future. A large task can be accomplished much more quickly in C than it can in Assembly, and the end result is a more intelligable and highly portable piece of code.If you only intend to play around with small tasks and experiment with quite simple problems then I agree with your decision not to bother learning C. But if you want to build on your skill and tackle bigger problems, then C is the way to go in my opinion. C is a tough learning curve, but I think it's worth it.Brian.

author: Zero999
author_rank: Super Contributor
timestamp: September 20, 2010, 02:41:04 pm
content: You're probably right, I'll probably give C another go at some stage but I'll stick with assembler for now because it forces me to learn the hardware.I don't think much of MPASM and preferred NASM because it has better macro support, context stack and local labels enabling one to cool things like:Code:Subroutine_1.loopsome code heregoto .loopSubroutine_2.loopsome code heregoto .loopThe trouble with NASM is it's x86 only.If there's an MCU assembler that can do that and is preferably free, I'd like to know.

author: Thermal Runaway
author_rank: Contributor
timestamp: September 20, 2010, 04:10:37 pm
content: I agree that it's worth familiarising yourself with assembly language (and, hence, the microcontroller architecture) before moving on. It's the longer way around, but it's definitely the best way to learn.Sorry I can't help you with assembler alternatives. I've always liked and used the MPLAB dev environment, although I don't use their C compiler (I use a third party compiler within the MPLAB environment).These days, even when I do write assembly, it's usually as part of a bigger C program so I just mix it in with my C code and let my C compiler take care of it.Good luck!Brian

author: Zero999
author_rank: Super Contributor
timestamp: September 20, 2010, 05:32:24 pm
content: Quote from: Thermal Runaway on September 20, 2010, 04:10:37 pmHave you used anything else before?I like the IDE, it's just the assembler which I think is lacking in features, I think if I hadn't used any other assembler then I'd probably like it, but features wise, going from NASM to MPASM is like going from Firefox 3.6 to Internet Explorer 6.NASM's macros were so good it could be made to be half way between a high level language and assembly, here's an example from the manual:http://www.nasm.us/doc/nasmdoc4.htmlCode:%macro if 1%push ifj%-1 %$ifnot%endmacro%macro else 0%ifctx if%repl elsejmp %$ifend%$ifnot:%else%error "expected `if' before `else'"%endif%endmacro%macro endif 0%ifctx if%$ifnot:%pop%elifctx else%$ifend:%pop%else%error "expected `if' or `else' before `endif'"%endif%endmacrocmp ax,bx; compare registers ax and bx, subtracts bx from ax, updates the flags (status register) but doesn't store the resultif aecmp bx,cxif ae; if above or equalmov ax,cxelsemov ax,bxendifelse; otherwisecmp ax,cxif aemov ax,cxendifendifThe same sort of thing can be done to make do, loop and exit structures which I used pretty frequently.

author: Jon Chandler
author_rank: Frequent Contributor
timestamp: September 21, 2010, 04:25:39 am
content: Basic works well for many applications including some complex stuff.The best basic compilers I have found are Swordfish Basic and Proton/Amicus. Swordfish works only with 18F-series parts, and the Special Edition is free with extremely generous limits. A free version of Proton Basic is available as Amicus - it's the full version of Proton, but limited to use with the 18F25k20 micro. The paid version of Proton supports 10F - 18F series parts.There's a brief discussionhere, and a variety of helpful information and guidancehere.Jon

author: Zero999
author_rank: Super Contributor
timestamp: September 21, 2010, 07:28:51 am
content: Quote from: Jon Chandler on September 21, 2010, 04:25:39 amI don't like the proprietary nature of any of the products linked to. I think this is why I'll learn C as a first language for microcontrollers because it's standardised and I've never really done it before. I already know BASIC and don't see the point in learning several variants of it for MCUs.At the moment I'll just see if there's a better assembler out there. I think someone should modify NASM for PICs but I accept it won't be an easy task.EDIT:For my next PIC project, I think I'll do a clock, a timer with a 7 segment LED display or something like that. I'll keep to the low end range of PICs for now and might even use a PIC16F54 as I have a few in my junk box.

author: darrylp
author_rank: Regular Contributor
timestamp: November 22, 2010, 09:38:26 pm
content: GC basic ( great cow basic ) is open source, and works well.having access to the source, means you can see how its working, and can modify itas needed.

author: scrat
author_rank: Frequent Contributor
timestamp: November 22, 2010, 10:59:19 pm
content: With C you have the possibility of going up to a high level (without loosing a low-level view), and portability is a real thing. For example, I used the same pieces of code (apart from some minor changes due to numeric types and optimized functions) for motor control on a Texas C2000 fixed-point, a PowerPC floating point and an Infineon Tricore with f.p.u. too.I doubt Basic can be worth for an EE, while perhaps an hobbist can quickly start developing with it.

author: Simon
author_rank: Unknown
timestamp: November 23, 2010, 06:28:14 am
content: Quote from: scrat on November 22, 2010, 10:59:19 pmthat does seem to sum up the situation very well

author: Zero999
author_rank: Super Contributor
timestamp: February 01, 2011, 01:17:59 am
content: It's been awhile but I've decided to continue with the project.Here's the schematic so far, I've not update the code yet but I thought I'd ask people their opinions of the schematic before going any further. I decided to go for a TRIAC and implement zero crossing. I decided using a relay and a transformer would be far too power hungry. The PIC12D509 uses a maximum of 400µA at 2.5V and the TL431 requires 1mA to regulate properly. I've simulated the capacitive power supply, at 230VAC the total RMS current draw is 3.44mA, the current through fluctuates between 1.09mA and 1.4mA with a constant current load of 400µA which simulates the MCU. The internal pull-ups will be disabled as I think they'll draw too much power.Now here is where I ask a question: look at how I've configured the TRIAC, is it all right?The TRIAC is the TICP206 which requires a trigger current of 10mA which I know is too high for the transformerless power supply but it it should only take 10mA for a few µs as once triggered, it will shoot up, cutting the gate off. The trouble is I'm not sure this will also happen when the mains goes negative. Will it blow the MCU and TRIAC gate or just draw too much current?If this is a problem I can change to the traditional low side appoach and set the MCU to only provide a pulse for a few µs after each zero crossing.What about the zero crossing input? Is it true that the PIC has internal protection diodes and 1M resistor will limit the current?http://www.datasheetcatalog.org/datasheet/PowerInnovations/mXtwqqx.pdfhttp://ww1.microchip.com/downloads/en/DeviceDoc/41236E.pdf

author: Mechatrommer
author_rank: Super Contributor
timestamp: February 01, 2011, 04:19:16 am
content: 1) i dont see the triac, but maybe its my noob. i guess its the D4 and D52) mcu pin connected to mains? seems dangerous to me, but then maybe i miss something.someone else could advice better.

author: scrat
author_rank: Frequent Contributor
timestamp: February 01, 2011, 12:50:46 pm
content: The 1M resistor is a quite standard way to sense voltage, AFAIK (I haven't done this yet). If you're worried about overvoltage, a diode or zener costs very little, isn't it?I'd change to the low-side, since otherwise the MCU won't be able to turn on the triac on one of the two halfwaves, and in the opposite case gate voltage will be very high.If you calculate the voltage drop on the 100uF cap for giving the gate charge required (at least 10mA for 20us), it results only 2mV. With the your gate resistance and diode current should be a higher ( (2.5-0.6) / 82 = 23mA), but voltage drop will be negligible too (provided that the on pulse is sufficiently narrow).

author: Zero999
author_rank: Super Contributor
timestamp: February 01, 2011, 05:47:43 pm
content: Quote from: scrat on February 01, 2011, 12:50:46 pmYou could be right. I'm not sure about this. I certainly agree with you about the gate voltage being really high when the mains is negative, but I don't see why it won't trigger. I'd rather have high side, I might couple it with a ferrite bead transformer (I know this works) which will allow me to use full wave rectification but it might be too much bother. I definitely won't build it without doing some experiments first at lower voltages. I'm going to bread board it running from a 12V transformer with the ballast capacitor and resistors replaced with suitable values, then when I assemble it on a PCB, I'll use an isolation transformer until I'm sure it's working as expected.QuoteI think you missed the diode in series with the gate and that the gate voltage drop is 0.9V@10mA, I make it (2.5-0.7-0.9)/82 = 11mA but you're right, it doesn't matter as long as the pulse is narrow enough.Oh and do you think I'll need to add a snubber network or MOV? With it zero voltage switching I hope the back EMF won't be too bad.

author: osmosis321
author_rank: Regular Contributor
timestamp: February 02, 2011, 05:30:42 pm
content: I'm one of those that prefers C in general, but am quite happy to mix in a bit of assembly when there's a clear benefit to be had by doing so. The better the C compiler, the fewer those times occur.

author: scrat
author_rank: Frequent Contributor
timestamp: February 03, 2011, 11:38:34 am
content: Quote from: Hero999 on February 01, 2011, 05:47:43 pmOn a positive halfwave you'll be trying to impose a positive gate current using 2.5V on the gate but having a high voltage on the lower anode (the motor is running and has its B-EMF). Since the MCU is fed by a supply referenced to neutral, this won't work, IMHO.Quote from: Hero999 on February 01, 2011, 05:47:43 pmI missed the gate voltage, quite a big mistakeQuote from: Hero999 on February 01, 2011, 05:47:43 pmI'm not really experienced, but I think a little RC snubber would be a good thing there, since you don't know how current-voltage phase shift will be under all conditions.This could be useful, I'll take a look, since I'd like to learn it too:http://www.st.com/stonline/books/pdf/docs/6785.pdf

author: Zero999
author_rank: Super Contributor
timestamp: February 03, 2011, 06:44:50 pm
content: Quote from: scrat on February 03, 2011, 11:38:34 amYes, I forgot, the motor will act as a generator once running.QuoteLooks like a good idea but the trouble is, I've got no idea what the inductance of the motor is.I'm now thinking about how to perform the zero crossing which I've never done with an MCU. The mains is connected to an input via a 1M resistor so I'll get a 50Hz pulse train. It seems pretty simple, all it needs to do is wait for pin 7 to change state (from high to low or low to high), apply a >20µs pulse to the TRIAC and repeat until the TRIAC needs to be turned off.I realised one potential problem: on the falling edge the MCU will register a zero whilst the mains voltage is still slightly above zero so the trigger pulse will occur earlier than I would like. Here's my calculations which determine that it's not a problem, as long as the pulse is longer than 23.9µs but could someone please check them.I'll be using 220V for the mains as it's the bottom end of the tolerance in my area.v = instantaneous voltageVPK= peak mains voltaget = time, in this case I'll be calculating the time for zero crossing to occur after the PIC has registered a zero.F = frequencyVOFF= the mains voltage when the MCU will register a low, which is 0.15VDD= 0.15 * 2.5 = 0.375VTheta = angle inoVPK= 220V sqrt(2) = 311Vv = VPKsin ThetaRearrange to make Theta the subject:Theta = arcsin v/VPK= arcsin (0.375/311) = 0.07oTheta = F360tRearrange to make t the subject:t = Theta/(360F) = 0.07/(360*50) = 3.9µsSo it the gate of the TRIAC is held high for >23.9µs it won't matter and the capacitor is more than large enough not to make any difference. I'll make the gate pulses 25µs just to ensure the TRIAC always triggers consistently.I can't see an easy way to monitor the counter whilst doing zero crossing so I think I'll change the timer routines to use the mains frequency as it'll be more compatible with my zero crossing set up.

author: scrat
author_rank: Frequent Contributor
timestamp: February 03, 2011, 09:58:18 pm
content: My first thought was that there is a main(s) mistake in your calculation: the sensed voltage isn't really all the mains, since the MCU pin sees a voltage divider between 1M and Zin (otherwise there would be 300V on that pin!).While reading, I caught what I think is the real behaviour of the sense net: the MCU pin voltage can follows the mains voltage as soon as it goes over Vdd, then the protection diode start to clamp voltage, with a current which is at max (about) 300uA.So the truth stays in the middle, IMO: the voltage is the mains scaled by the ohmic divider (Zin, 1M), until Vdd is reached.The datasheet doesn't report an accurate value for Zin, but the leakage current is max 1uA, so impedance should be >5.5M, and then the ohmic divider is almost negligible for your purpose, or you could redo your calculation with a 5/6 scaling factor.

author: Zero999
author_rank: Super Contributor
timestamp: February 04, 2011, 08:19:56 pm
content: 311*5.5/6.5 = 263VTheta = arcsin (0.375/263) = 0.082ot = 0.082/(360*50) = 4.56µsSo it appears 25µs is more than enough leeway and in any case, I think 1µA is a worst case scenario, a PIC has CMOS inputs which can be taken to be open circuit for all intents and purposes.

author: scrat
author_rank: Frequent Contributor
timestamp: February 05, 2011, 09:07:18 am
content: With the available data, I think this is the best you can do to estimate that delay.However, should it be even 10 times higher, it would represent about 1.25% of one period.Is the controller intended to chop the mains for variable speed or simply an on/off control?

author: Zero999
author_rank: Super Contributor
timestamp: February 05, 2011, 10:41:15 am
content: It's simply and on/off control.

author: scrat
author_rank: Frequent Contributor
timestamp: February 05, 2011, 10:44:33 am
content: One reason more for trying a snubber and not worry about a 25us delay.However, delay is a thing you can measure without even programming the PIC nor switching the triac, so you can go quite safe then.

author: Zero999
author_rank: Super Contributor
timestamp: February 06, 2011, 12:20:21 pm
content: I won't be delaying the pulse, just making it 5µs longer than it needs to be.How will a snubber make any difference to the triggering? I thought it only improved the turn off.I was thinking about doing in circuit programming but then I realised it would be a pain. I'd have to put a diode in series with D3 and between D3 and C3 so the programmer's VDD isn't clamped to 2.5V and changing some of the IO pin positions. I decided it's not worth the bother, after all, once the PIC is programmed and debugged on a breadboard, it will never be changed.

author: scrat
author_rank: Frequent Contributor
timestamp: February 06, 2011, 10:18:37 pm
content: I was thinking about measurign the actual delay between mains zero crossing and 0.375V crossing on the MCU pin.My sentence about the snubber was quite confused, I'm not sure if what I was trying to say is right. However, you're right, it has nothing to do with triggering.With almost any programmer you should be able to self-power the device circuit, at least by disconnecting the Vdd pin.In-circuit programming is perhaps overkill for the purpose, but maybe in the future you could use the same circuit for something more complicated...