AC PHASE CONTROL

Post here first, or if you can't find a relevant section!
Post Reply
Liam
Posts: 3
Joined: Wed Apr 13, 2022 6:21 am

AC PHASE CONTROL

Post by Liam »

Can anybody help me to transform an arduino code into a stm32duino?



// This Arduino sketch is for use with the heater
// control circuit board which includes a zero
// crossing detect function and an opto-isolated TRIAC.
//
// AC Phase control is accomplished using the internal
// hardware timer1 in the Arduino
//
// Timing Sequence
// * timer is set up but disabled
// * zero crossing detected on pin 2
// * timer starts counting from zero
// * comparator set to "delay to on" value
// * counter reaches comparator value
// * comparator ISR turns on TRIAC gate
// * counter set to overflow - pulse width
// * counter reaches overflow
// * overflow ISR turns off TRIAC gate
// * TRIAC stops conducting at next zero cross


// The hardware timer runs at 16MHz. Using a
// divide by 256 on the counter each count is
// 16 microseconds. 1/2 wave of a 60Hz AC signal
// is about 520 counts (8,333 microseconds).


#include <avr/io.h>
#include <avr/interrupt.h>

#define DETECT 2 //zero cross detect
#define GATE 9 //TRIAC gate
#define PULSE 4 //trigger pulse width (counts)
int i=483;

void setup(){

// set up pins
pinMode(DETECT, INPUT); //zero cross detect
digitalWrite(DETECT, HIGH); //enable pull-up resistor
pinMode(GATE, OUTPUT); //TRIAC gate control

// set up Timer1
//(see ATMEGA 328 data sheet pg 134 for more details)
OCR1A = 100; //initialize the comparator
TIMSK1 = 0x03; //enable comparator A and overflow interrupts
TCCR1A = 0x00; //timer control registers set for
TCCR1B = 0x00; //normal operation, timer disabled


// set up zero crossing interrupt
attachInterrupt(0,zeroCrossingInterrupt, RISING);
//IRQ0 is pin 2. Call zeroCrossingInterrupt
//on rising signal

}

//Interrupt Service Routines

void zeroCrossingInterrupt(){ //zero cross detect
TCCR1B=0x04; //start timer with divide by 256 input
TCNT1 = 0; //reset timer - count from zero
}

ISR(TIMER1_COMPA_vect){ //comparator match
digitalWrite(GATE,HIGH); //set TRIAC gate to high
TCNT1 = 65536-PULSE; //trigger pulse width
}

ISR(TIMER1_OVF_vect){ //timer1 overflow
digitalWrite(GATE,LOW); //turn off TRIAC gate
TCCR1B = 0x00; //disable timer stopd unintended triggers
}

void loop(){ // sample code to exercise the circuit

i--;
OCR1A = i; //set the compare register brightness desired.
if (i<65){i=483;}
delay(15);

}
ag123
Posts: 1655
Joined: Thu Dec 19, 2019 5:30 am
Answers: 24

Re: AC PHASE CONTROL

Post by ag123 »

try a blink sketch firsthand
https://github.com/stm32duino/wiki/wiki

and later review the API and examples for hardware timer.
https://github.com/stm32duino/wiki/wiki ... er-library

playing with timers is something like

Code: Select all

    MyTim->pause();    
    MyTim->setMode(channel, TIMER_OUTPUT_COMPARE_PWM1, pin);
    // MyTim->setPrescaleFactor(8); // Due to setOverflow with MICROSEC_FORMAT, prescaler   will be computed automatically based on timer input clock
    MyTim->setOverflow(100000, MICROSEC_FORMAT); // 10000 microseconds = 10 milliseconds
    MyTim->setCaptureCompare(channel, 50, PERCENT_COMPARE_FORMAT); // 50%
    MyTim->attachInterrupt(Update_IT_callback);
    MyTim->attachInterrupt(channel, Compare_IT_callback);
    MyTim->refresh();
    MyTim->resume();
and lookup ST web and download and review the ref manual for your mcu (especially the Timers) and keep it handy.
Liam
Posts: 3
Joined: Wed Apr 13, 2022 6:21 am

Re: AC PHASE CONTROL

Post by Liam »

Thanks for your answer. Firstable, i want to say that i am using a nucleo 144 (STM32F429ZIT6U processor). IT is my first time that use a stm32. I have tried a blink examples and work very fine, but iam not able to make the control of the phase for the thyristor or triac. I just need some help to get start with the code.
ag123
Posts: 1655
Joined: Thu Dec 19, 2019 5:30 am
Answers: 24

Re: AC PHASE CONTROL

Post by ag123 »

i'm not too familiar with atmega328, but I'd give it a try.

It seemed you are trying to detect a 'zero crossing' and using that to toggle the pins i.e. triac on or triac off.

In practical terms, stm32 timers did not define that trigger point for the timer inputs, it is some voltages (between 0 - vdd (3.3v) at the rising edge which would trigger a timer. i.e. this isn't trying to detect zero crossing and that there is no comparator, unless you choose a stm32 soc that specifically has a comparator in it.

A close substitute is the ADC. you could do things like

Code: Select all

uint8_t inputPin = PA0;
uint8_t controlPin = PAxx; //choose a preferred pin

HardwareTimer *MyTim = new HardwareTimer(TIM1);
uint8_t channel = 1;

uint8_t state = LOW;

void timer_callback()
{
	MyTim->pause();    // stop the timer
	digitalWrite(controlPin, LOW);
	state = LOW;
}

void setup() {
	pinMode(inputPin, INPUT_ANALOG);
	
	MyTim->pause();    
	MyTim->setMode(channel, TIMER_OUTPUT_COMPARE_PWM1, pin);
	// MyTim->setPrescaleFactor(8); // Due to setOverflow with MICROSEC_FORMAT, prescaler   will be computed automatically based on timer input clock
	// set the period with setOverflow and a duty cycle with setCaptureCompare
	MyTim->setOverflow(100000, MICROSEC_FORMAT); // 10000 microseconds = 10 milliseconds
	MyTim->setCaptureCompare(channel, 50, PERCENT_COMPARE_FORMAT); // 50%

	MyTim->attachInterrupt(channel, timer_callback);
	
	pinMode(controlPin, OUTPUT);
}

uint16_t my_preferred_duty_cycle = 10;
uint16_t threshold  = (uint16_t) ( 0.5 / 3.3 * 4096);

void loop() {
	uint16_t volt = analogRead(pin);
	if (volt > threshold  && state == LOW) { // lets say 0.5v is our threshold
		// start the timer
		MyTim->setCaptureCompare(channel, my_preferred_duty_cycle, PERCENT_COMPARE_FORMAT);
		MyTim->refresh();
		
		digitalWrite(controlPin, HIGH);
		MyTim->resume();
		state = HIGH;
	}
}
Note that the above code is rather reckless and can be a danger in your use case.
The idea is that in loop(), the code check for the input pin voltage to be > 0.5, if that is true, it toggles the control pin high and start the timer.
Then when the duty cycle duration is up, timer interrupt fires (please check this, could be incorrect).
The interrupt code toggles the control pin low and pause the timer.

This code won't work in your use case, it is wrong. You would need to design how you detect that threshold crossing, e.g. with the ADC.
(edit: ^ i added state tracking in the code above, makes it 'level triggered' kind of, but double check your own implementation)
And do remember that most stm32 ADC input channels can take between GND 0v and a max of around 3.3v VDD, anything higher, and you risk destroying the chip/SOC.

This code would also likely have quite significant latency, as analogRead() has quite a bit of overheads.
STM32 timers are elaborate hardware, and actually you can use the timer output as the output pin itself
(i.e. the timer output HIGH/LOW is directly at the pin itself, this saves that additional digitalWrite()).
There is also things like 'one pulse' mode, this requires tweaking some h/w timer registers as the api may not have all the options.
Review the ref manual if you'd like to dive into that.

As for the analogRead() latency, stm32 has something called 'analog watchdog', it is hardware based level setting and triggering.
So that the ADC hardware can fire interrupts on crossing the levels. Using them would need to access the HAL libraries, or resort to using ADC registers. There are some examples of those 'HAL' access here:
https://github.com/stm32duino/STM32Exam ... annels.ino
there is a better one, for analog watchdog here:
viewtopic.php?f=41&t=110
again review the ref manual for your chip
Post Reply

Return to “General discussion”