SCR gate controller circuit using arduino - Page 1
Forum section: electronics / projects

author: nesh_reds
author_rank: Newbie
timestamp: April 12, 2013, 04:33:40 pm
content: Greetings everyone. I m doing a project based on SCR gate controller circuit for a FW H bridge rectifier. My objectives is to fire up to 4 pulses for the SCR with 2 for each cycles. I also must be able to vary the firing angle of the SCR. I decided to use Arduino Duemilanove recomended by my friend. Based on a journal i came across, i followed up its method and designed the program. I need a small favour to check if my program is correct and any correction or suggestion please.1) When the rising edge of the zero crossing is detected from the AC mains, the arduino will be interupted and it will manipulating the firing delay to determine the firing angle of the pulse. on LCD, the firing angle "alpha" and delay "d" will be displayed.2) I m using ADC method to calculate the alpha. So by using a potentiometer, i m converting the 0-5V of the analog reference voltage of the arduino into the 0-1023 count. by using this method, i can calculated the alpha by using the formulaalpha= ADC/5.683) now to calculate the delay "d". The AC supply is 50Hz so it has a period of 20ms. Therefore for +ve cycle will be 10ms delay is required. . so the max value of ADC with +5v reference will be 1023 which the 10ms. I have to do this for both negative n positive cycle. SO the delay calculations isd= (ADC*5)*1.955the 1.955 is the scaling factor fot the ADC.So from this method, i have done the program with 2 outputs one for postive cycle an the other for negative cycle. By using a pulse transformer, the output will be converted into two which also provides isolation btwn the rectifier and arduino.I realise that i might be wrong at my negative cycle which is adc_pot2. I have also attached my zero cross circuit and its output. So i require a little bit of help here. So this is my program:#include <LiquidCrystal.h>/*The circuit:*LCD RS pin to digital pin 8*LCD Enable pin to digital pin 9*LCD D4 pin to digital pin 4*LCD D5 pin to digital pin 5*LCD D6 pin to digital pin 6*LCD D7 pin to digital pin 7*LCD R/W pin to ground*/LiquidCrystal lcd(8, 9, 4, 5, 6, 7);int ZCDpin= A0;int ADCpin= A1;int SCRpin1= 9;int SCRpin2= 10;int alpha;int d;void setup (){attachInterrupt (ZCDpin, loop, RISING);pinMode (SCRpin1, OUTPUT);pinMode (SCRpin2, OUTPUT);lcd.begin (16, 2);lcd.clear ();analogReference (DEFAULT);}void loop (){if (ZCDpin == 1){adc_pot1();}else if (ZCDpin == 0){adc_pot2();}lcd.setCursor (0,0);lcd.print("Angle= ");lcd.print(alpha);lcd.setCursor (0,1);lcd.print("delay= ");}void adc_pot1(){{ADC= analogRead(ADCpin);alpha = ADC/5.68;d = (ADC*5)*1.955;delay (alpha);digitalWrite(SCRpin1, HIGH);delay (d);}}void adc_pot2(){{ADC= analogRead(ADCpin);alpha = ADC/5.68;d = (ADC*5)*1.955;delay (alpha);digitalWrite(SCRpin2, HIGH);delay (d);}}

author: Niklas
author_rank: Frequent Contributor
timestamp: April 12, 2013, 10:03:04 pm
content: Do you have any isolation, ie optocoupler, between the Arduino and the SCR?

author: Psi
author_rank: Super Contributor
timestamp: April 12, 2013, 10:17:12 pm
content: yeah, i would recommend isolation as well.

author: free_electron
author_rank: Super Contributor
timestamp: April 12, 2013, 11:34:02 pm
content: and how much time do the a/d conversions take ? and how much time is spent in calculating ?and how much in LCD update ?all that potmeter sampling , lcd update etc needs to be done outside the interrupt

author: Rufus
author_rank: Super Contributor
timestamp: April 13, 2013, 12:56:43 am
content: Quote from: nesh_reds on April 12, 2013, 04:33:40 pmNot even close I am afraid.I know nothing about Arduino but I assumeattachInterrupt (ZCDpin, loop, RISING);Configures an interrupt on the rising edge of a singal on the ZCDpin and calls the function loop in the interrupt handler. You will only get one rising edge per mains cycle from your detector, when you get to loop() (poor choice of name because it isn't and doesn't loop) ZCDpin will always be high and you will only ever call adc_pot1().The delay(d) does nothing but waste time before returning from the interrupt. You only ever set the SCRpinx high, they will go high once and stay there. Significant time will be taken reading the ADC and calculating alpha and d before the delay even starts. Spewing stuff to the LCD is likely very slow and delays returning from the interrupt which in some circumstances will delay taking the next one. There is no main() so I don't see how it will even compile, but, I know jack about Arduino.You should look at starting a hardware timer in the zero crossing interrupt and have the interrupt handler for the timer generate the pulses. Do the ADC, calculating and LCD stuff in the foreground. You will need to be a little careful about getting the calculation results in the interrupt handler.

author: mjkuwp
author_rank: Unknown
timestamp: April 13, 2013, 04:13:45 am
content: in Arduino Loop, loops. so that part is OK.I agree the Delay is not a very nice function. I don't like how Arduino handles time so I usually change how the timers run which means I end up breaking a few things. Delay just hogs the processor for a while, not such a nice thing to do.Using Arduino without breaking it - many people would use millis() and check against it to see how much time has passed.

author: nesh_reds
author_rank: Newbie
timestamp: April 13, 2013, 05:19:52 am
content: Quote from: mjkuwp on April 13, 2013, 04:13:45 amCan you explain to me abit more? I am kinda new to the arduino. An example mayb?

author: Kremmen
author_rank: Super Contributor
timestamp: April 13, 2013, 05:59:47 am
content: The structure of the program is both incorrect and in places suboptimal.Firstly, the interrupt is directedd at "loop" but that is a reserved Arduino member function. "Out of the box" the Arduino code will run loop repeatedly by calling it from behind the screens. This will be easily apparent if you go and check the library source code. So, "loop" is an inappropriate target for an interrupt.Secondly, you have the ADCPot function repeated twice with exatly the same code and only slightly differing data. This is exactly why the concept of a subroutine has been invented in the first place. So by all means learn to do it right: code only once and insert the differences as arguments.Thirdly, it looks like you are trying to use the edge direction of the zero crossing detector as information on which half cycle is starting. Tha can work but not the way you are doing it; the interrupt must be attached as signal CHANGE, not RISING. So you will get an interrupt when the detector signal transitions from 0 to 1 and another when it goes back to 0.Change your program stgructure as follows:In the setup() do whatever initialization you need.In the loop() function, do the A/D conversions, firing angle calculations and and firing delay.- If the A/D conversions are done to read a pot turned by human hand, you don't need to be especially fast. You may want to aleternate reading the channels say every 10 iterations or so. Doing it more often probably aids nothing.- Based on the returned A/D value calculate the number of microseconds to the firing pulse. You may want to account for the sine curve to linearize the relationship between A/D value and output voltage, maybe not.- compare the value returned by the built-in function "micros()" to the desired instance of firing and when reached, fire away. This part would be done best using hardware support but that is not directly supported by the Arduino environment. You can do it without too much trouble but for that you must program the ATmega processor directly. Can be done, you just need to read the datasheet a bit.Write a new function that takes no arguments and name it appropriately. Interrupt functions are often called handlers so i suggest something descriptive like "ZChandler()" In the interrupt handler do the delay counter reset for firing pulse timing. As a general rule all interrupt handlers should process quickly and not contain any delaying elements such as long loops or time delays. While the handler is running, all other program execution is suspended - especially the firing angle delay above. For that reason delay counting in hardware is generally a good idea.- When zero crossing is detected, get the value of micros() and store it for delay calculation as explained above. By comparing this stored value repeatedly with the current value of micros(), the moment of firing can be calculated easily.

author: nesh_reds
author_rank: Newbie
timestamp: April 13, 2013, 06:10:15 am
content: Mr Kremmen... Thank you so much.... I understand it better now.. I shall re-editing the codes.. After i m done can i refer it back to u if possible?

author: Kremmen
author_rank: Super Contributor
timestamp: April 13, 2013, 08:33:58 am
content: Sure why not. Of course there are others equally able to do that but no problemo, i can do it.

author: Niklas
author_rank: Frequent Contributor
timestamp: April 13, 2013, 09:10:54 am
content: Alphanumeric LCDs are generally quite slow compared to your micro but there are some tricks available to deal with it.Must the whole display be updated every time?In this case you might get away with only updating the numbers instead of all text. About half as many characters to write, but still you have two cursor moves left.How often do you need to update the display?Updating it 50 or 100 times per second is overkill. Maybe just 5 or 10 times per second is a better update rate and will result in less display flicker. The you can divide the complete display handling into smaller pieces and put them in a switch case-structure instead. The idea is to use the update loop time as a delay instead of the built in one in lcd.setcursor(). You will probably have to write your own version of lcd.setcursor() and just skip the delay part.Code:switch(LCDState){case 0:lcd.setcursor(0,0);break;case 1:lcd.print("Angle= ");break;case 2:lcd.print(Angle);break;case 3:lcd.setcursor(0,1);break;case 4:lcd.print("phase= ");break;case 5:lcd.print(phase);break;default:break;}if(LCDState < UpdatePeriod)LCDState++;elseLCDState = 0;Third question is about resolution. How much will the user be affected if you change from ADC*5*1.955 to ADC*5*2= ADC*10? If you want to save processor execution time, skip float! Depending on how the compiler and the micro handles multiplication, you might get an improvement by doing some of the math as ADD and Bit shifting.Code:A = Value << 1; // A=Value*2B = A << 2; // B = A * 4 = Value*8A = A+B; // A = Value*2 + Value*8 = Value*10Also check if you can convert the ADC/5.68 to something like (ADC*N)/2^n. N is an integer (non float) and 2^n is 2,4,8,16,32 etc. Division in combination with float will be a tough job to do fast for the Arduino.Code:A = Value * N;A = A >> n; // n is the same as in 2^n

author: Rufus
author_rank: Super Contributor
timestamp: April 13, 2013, 02:44:33 pm
content: Quote from: mjkuwp on April 13, 2013, 04:13:45 amHeh, well it still isn't and doesn't. I assume it is called from a loop. So it is still a dumb name - something the Arduino lot seem to specialise in.

author: nesh_reds
author_rank: Newbie
timestamp: April 19, 2013, 03:55:08 pm
content: Hello Mr Kremmen... I followed your step but my output x worked.. Yet again its the negative cycle that causes all the problem. Yet my potentiometer isnt doing anything. and as for the micros() function. I seriously don't understand on implementing it.#include <LiquidCrystal.h>/*The circuit:*LCD RS pin to digital pin 8*LCD Enable pin to digital pin 9*LCD D4 pin to digital pin 4*LCD D5 pin to digital pin 5*LCD D6 pin to digital pin 6*LCD D7 pin to digital pin 7*LCD R/W pin to ground*/LiquidCrystal lcd(8, 9, 4, 5, 6, 7);byte degree ={B00110,B01001,B01001,B00110,B00000,B00000,B00000,};int ZCDInt= 0; //pin2int potPin= A0;int scrPin1= 10;int scrPin2= 11;int sensorVal= 0;int outVal= 0;int alpha;int d1;int d2;void setup (){attachInterrupt (ZCDInt, zero_cross, CHANGE);pinMode (scrPin1, OUTPUT);pinMode (scrPin2, OUTPUT);lcd.begin (16, 2);lcd.clear ();lcd.print("FYP 2013/2013");}void loop (){int sensorVal = analogRead(potPin);outVal = map(sensorVal, 0, 1023, 0, 1023);alpha = sensorVal/5.68;d1 = (sensorVal*5)*1.955d2 = (sensorVal*5)*1.955*2lcd.setCursor(0,1);lcd.print("a: ");lcd.print(alpha);lcd.print(0);lcd.setCursor(8,1);lcd.print("d: ");lcd.print(d1);lcd.print("ms");}void zero_cross(){if (ZCDInt=-HIGH){fire_scr1();}else{fire_scr2();}}void fire_scr1(){delay(d1);digitalWrite(scrPin1, HIGH);delay(500);digitalWrite(scrPin1, LOW):}void fire_scr2(){delay(d2);digitalWrite(scrPin2, HIGH);delay(500);digitalWrite(scrPin2, LOW):

author: Kremmen
author_rank: Super Contributor
timestamp: May 02, 2013, 06:55:19 am
content: I won't comment on the LCD display code, assuming it is a standard library and should work.You seem to use a map() function that does nothing. Mapping 0-1023 to 0-1023 is a null operation that doesn't do anything...However, the main issue is with the structure of your code. Now you have attached an interrupt handler to the zero crossing detection input which is OK, but what is not OK is that you then have a busy wait inside the interrupt handler code (the delay function is a busy wait). This is a program structure no-no. You do understand how an interrupt handler works, do you? When the interrupt signal occurs ( in this case pin 2 changes state) the processor stops whatever it is doing, saves the register context into the stack and jumps to the interupt handler code. While executing the handler, everything else is suspended until the handler completes, register context is restored and execution continues from the point where it was interrupted. If you now have a wait loop _inside_ the handler, everything else is standing still while the delay runs. This is considered bad since you might miss all kinds of other events because the relevant code is not executing. For that reason interrupt handler should be strictly as short as feasible, and definitely not introduce delays on purpose.You may get away with it in this particular case since the code does not have anything else to do, but generally, no.The proper way to implement this kind of functionality would be to set up a hardware timer to count down the trigger delay and fire the pulse from hardware. It is entirely possible using the Mega328 processor powering the Arduino, but i haven't checked whether the Arduino standard libraries include that kind of functionality. My previous suggestion was the second best alternative that does introduce some jitter but perhaps not too much. It was based on using the Arduino standard timer. There are 2 access functions, millis() and micros() that return the current millisecond or the current microsecond since previous start. You can implement a time delay in the loop() by reading an initial timer value and then comparing the current value to the initial until the difference reaches your delay. Then the delay time has passed from the initial timer value. Doing that in the loop() will let everything else proceed as well.

author: nesh_reds
author_rank: Newbie
timestamp: May 07, 2013, 01:22:53 pm
content: Hello Mr Kremmen..... But i need an urgent help.. I have done my program and had the output from arduino. I have problem that the firing point is faster then usual. . I am using a potentiometer to vary the pulse position... wen i start varying the potentiometer, the skips around 30 degrees. Eg, at 90 degree's of the sine.. i m getting 60 degrees displayed on my LCD... I have included the pic of my output pulse on 0 degrees.. I hope I can guided on this... this is my output pic and code... I don't really have much idea on implementing the micros() function....#include <LiquidCrystal.h>/*The circuit:*LCD RS pin to digital pin 8*LCD Enable pin to digital pin 9*LCD D4 pin to digital pin 4*LCD D5 pin to digital pin 5*LCD D6 pin to digital pin 6*LCD D7 pin to digital pin 7*LCD R/W pin to ground*/LiquidCrystal lcd(8, 9, 4, 5, 6, 7);byte degree = {B00110,B01001,B01001,B00110,B00000,B00000,B00000,};int zcdPin = 0; //pin 2int potPin = A0;int sensorValue = 0;int outputValue = 0;int alpha;int d;int scrPin1 = 10;int scrPin2 = 11;void setup (){lcd.begin (16, 2);lcd.print("FYP 2012/2013");lcd.createChar(1, degree);attachInterrupt (0, ZCHandler, FALLING);}void loop (){sensorValue = analogRead(potPin);alpha = sensorValue/5.68;d = (sensorValue*5)*1.955;lcd.setCursor(0,1);lcd.print("a:");lcd.print(alpha);lcd.write(1);lcd.setCursor(8,1);lcd.print("d:");lcd.print(d);lcd.print("ms");}void ZCHandler(){digitalWrite(scrPin2, LOW);delayMicroseconds(d);digitalWrite(scrPin2, HIGH);delayMicroseconds(1000);digitalWrite(scrPin2, LOW);}