Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 8 of 8

Thread: AC Mains Cycle Phase Control

  1. #1
    Senior Member
    Join Date
    Feb 2017
    Posts
    276

    AC Mains Cycle Phase Control

    Hi. Iím wondering if itís possible to do 60 Hz AC mains phase control (i.e. incandescent light dimming) using the T3.x FlexTimer Module (FTM) without direct software intervention at the per-cycle time scale.

    Iíve scanned the FTM section in the datasheet and see that it has lots of different modes that Iíll go back and study in detail. But, if someone can say for certain that it CANíT do what Iím proposing, then Iíll save the effort for now.

    My basic idea is to start the FTM counting (from 0) when the AC cycle zero crossing is detected. The match value (CnV) and terminal count (MOD) will have already been set up by software based on the amount of dimming desired. When the FTM counter reaches CnV, an output pulse will be begun to trigger the triacís gate at the desired phase of the AC cycle. Shortly thereafter, MOD count will be reached, and the timer will overflow. This will terminate the output pulse so that the triacís gate is not asserted for when the current half cycle ends.

    Key to this idea is being able to sync the start of FTM counting to the AC cycle zero crossing and to ensure that counting does not start again after overflow until the next crossing occurs.

    As an alternative, Iím wonder if I could sync one of the PWM modes to the AC cycle zero crossing and adjust the PWM output characteristics to get the same behavior.

    So, does anyone know for sure that this is not possible?

    Thanks.

  2. #2
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    19,927
    Sounds like it could probably work, restarting the FTM when you detect the zero cross.

  3. #3
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    19,927
    You might also consider just doing it all by software. Here's a project I did 4 years ago, which ran on Teensy 2.0, controlling 4 dimmers using AC zero cross detection and just computing everything in terms of microseconds.

    https://www.pjrc.com/embrace-heart-lighting/

    About half way down the page is a video of it fading the bulbs.

  4. #4
    Senior Member
    Join Date
    Feb 2017
    Posts
    276
    Hi Paul, thanks for the reply. Yes, Iím sure T3.x is more than capable of doing this in software. Iím pursuing the hardware technique as somewhat of a learning exercise.

    Do you know if FTM can be operated in the mode I outlined? Mainly:

    * Start counting when triggered by external pulse.

    * Stop counting after overflow and donít start again until the next trigger.

  5. #5
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    19,927
    Quote Originally Posted by gfvalvo View Post
    * Start counting when triggered by external pulse.
    Maybe, but I have not ever tried this. I would use an interrupt and have the interrupt code configure the timer.

    The FTM timers do have special fault inputs. I've never used them, so I really can't suggest anything more specific than reading the FTM chapter carefully.


    * Stop counting after overflow and don’t start again until the next trigger.
    Again, I've almost always used the FTM timers in free running mode, so really not sure about this.

    Personally, I would not do things this way, starting and stopping the timer. I would configure it to count from 0 to 0xFFFF and roll over. Then I'd use 1 channel as input capture, to capture the free-running count at the zero cross detection. Then I'd use another channel as output compare, to generate the pulse at a desired time in the future... where the input capture interrupt programs the output compare channel. Then I'd have that channel interrupt, and in that ISR I'd reprogram it to again change the pin in the future, for the other edge of the pulse. This way does involve interrupts for every event, but the hardware does the actual work precisely aligned to the timer count and the interrupts clean up after each event and program what happen at the next event.

  6. #6
    Senior Member
    Join Date
    Feb 2017
    Posts
    276
    Thanks for the suggestion, Paul.

    I do see at least one advantage to doing it your way - I could use the FTM counter value captured at every zero crossing to determine the half-cycle period of the AC line in units of counter ticks. That would make it easy to scale the desired brightness into counts for the output compare event. This way I would not have to determine a priori the number of counts that occur during a half cycle. It would also automatically account for drift and unit-to-unit variation of the T3.x’s clock rate.

  7. #7
    Senior Member+ Theremingenieur's Avatar
    Join Date
    Feb 2014
    Location
    Colmar, France
    Posts
    2,384
    Quote Originally Posted by PaulStoffregen View Post
    Personally, I would not do things this way, starting and stopping the timer. I would configure it to count from 0 to 0xFFFF and roll over. Then I'd use 1 channel as input capture, to capture the free-running count at the zero cross detection. Then I'd use another channel as output compare, to generate the pulse at a desired time in the future... where the input capture interrupt programs the output compare channel. Then I'd have that channel interrupt, and in that ISR I'd reprogram it to again change the pin in the future, for the other edge of the pulse. This way does involve interrupts for every event, but the hardware does the actual work precisely aligned to the timer count and the interrupts clean up after each event and program what happen at the next event.
    Sounds like the most reliable solution to me.

  8. #8
    Senior Member
    Join Date
    Feb 2017
    Posts
    276
    So, as a learning exercise, I decided to see if I could make it work using only the hardware without firmware involvement. Turns out, itís possible. See code below. The firmwareís only jobs are initial set up and updating 2 registers when a phase change is required. Other than that, the hardware does all pulse generation on its own.

    The setup runs two FTM channels in ďCombine ModeĒ to generate the brief pulse that will trigger the triac. The FTMís counter is reset on every zero crossing of the AC cycle (via hardware synchronization mode) to synch it up. The zero crossing detect signal is feed into the Analog Comparator which issues a hardware synch trigger on every rising edge. I needed to use the CMP because the other processor pins that are capable of generating an FTM hardware sync trigger are already used on the T3.2 board for the 16MHz xtal.

    I donít have the AC mains circuitry connected yet, Iím simulating the zero crossing detect with a pulse stream generated on another board. The pulses (yellow in the scope shots below) repeat at 120 Hz. I arbitrarily set their width to 500us. Iím sure thatís way too wide and Iíll need to check operation with much narrower pulses as I suspect the actual circuitry will produce.

    The triac trigger signal (green in scope shots) is set to 10us, again an arbitrary number. The scope shots show 3 different positions of the trigger based on desired phase of the triacís activation (low, medium, high brightness).

    Since the counter should never overflow in normal operation, I plan to use the TOF interrupt to indicate loss of zero crossing detect pulses and shut down the triac until they return.

    Code:
    #include <Arduino.h>
    
    void initCmp();
    void initFtm();
    
    volatile bool ftmOverflow = false;
    
    #define FTM1_CH0_PIN 3
    #define FTM1_CH1_PIN 4
    
    void setup() {
    	Serial.begin(115200);
    	delay(2000);
    	Serial.println("Starting");
    
    	initCmp();
    	initFtm();
    
    }
    
    void loop() {
    	bool newSetting = false;
    	static int16_t setting = 0;
    
    	while (Serial.available()) {
    		char c = Serial.read();
    		switch (c) {
    			case '+':
    				newSetting = true;
    				setting += 20;
    				if (setting > 255) {
    					setting = 255;
    				}
    				break;
    
    			case '-':
    				newSetting = true;
    				setting -= 20;
    				if (setting < 0) {
    					setting = 0;
    				}
    				break;
    
    			default:
    				break;
    		}
    		if (newSetting) {
    			Serial.print("Set Point = ");
    			Serial.println(setting);
    			uint32_t turnOnPoint = (255 - setting) * 192;
    			FTM1_SYNCONF &= ~FTM_SYNCONF_HWWRBUF;
    			FTM1_C0V = turnOnPoint;
    			FTM1_C1V = turnOnPoint + 60;
    			FTM1_SYNCONF |= FTM_SYNCONF_HWWRBUF;
    		}
    
    	}
    
    	if (ftmOverflow) {
    		ftmOverflow = false;
    		Serial.println("TOF");
    	}
    }
    
    void ftm1_isr() {
    	if (FTM1_SC & FTM_SC_TOF) {
    		ftmOverflow = true;
    		FTM1_SC &= ~FTM_SC_TOF;
    	}
    }
    
    void initCmp() {
    	CORE_PIN11_CONFIG = PORT_PCR_MUX(0);			// Set Pin 11 for CMP0 IN0
    	SIM_SCGC4 |= SIM_SCGC4_CMP;						// Enable clock to CMP
    	CMP0_CR1 = 0;									// Reset control registers
    	CMP0_CR0 = 0;
    	CMP0_CR1 = CMP_CR1_PMODE | CMP_CR1_EN;			// Enable CMP, High Speed mode
    	CMP0_FPR = 0;									// Filter off
    	CMP0_DACCR = CMP_DACCR_DACEN | CMP_DACCR_VRSEL | CMP_DACCR_VOSEL(0x1F);		// Compare Pin 11 to DAC Output
    	CMP0_MUXCR = CMP_MUXCR_PSEL(0) | CMP_MUXCR_MSEL(7);							// Enable and setup DAC
    	CMP0_SCR = CMP_SCR_CFR | CMP_SCR_CFF;										// Clear status flags
    }
    
    void initFtm() {
    	pinMode(FTM1_CH0_PIN, OUTPUT);
    	digitalWrite(FTM1_CH0_PIN, LOW);
    
    	FTM1_SC = FTM1_SC & 0;    // Reset counter control, Force read-modify-write to clear TOF flag
    	FTM1_MODE = FTM_MODE_FTMEN; // Enhanced FTM Mode
    	FTM1_OUTINIT = 0;     // All channel outputs initialized to low
    
    	FTM1_CNT = 0000;      // Reset counter
    	FTM1_MOD = 0xFFFF;      // Terminal count
    	FTM1_CNTIN = 0;       // Counter reload value
    
    	NVIC_SET_PRIORITY(IRQ_FTM1, 48);
    
    	FTM1_C0SC = FTM1_C0SC & 0;                    // Force read-modify-write, clears CHF flag
    	FTM1_C0SC = FTM_CSC_MSB | FTM_CSC_ELSB;      // Active high CH0 output pulses in combined mode
    	//FTM1_C0V = 48960;                       // CH0 output compare value
    	FTM1_C0V = 0;                       // CH0 output compare value
    
    	FTM1_C1SC = FTM1_C1SC & 0;                    // Force read-modify-write, clears CHF flag
    	FTM1_C1SC = FTM_CSC_MSB;             // Active high CH1 output pulses in combined mode
    	//FTM1_C1V = 48960;                       // CH1 output compare value
    	FTM1_C1V = 0;                       // CH1 output compare value
    
    	FTM1_COMBINE = FTM_COMBINE_COMBINE0;              // Combine channels 0 and 1
    	FTM1_COMBINE |= FTM_COMBINE_SYNCEN0;              // Enable sync CV0, CV1
    
    	*portConfigRegister(FTM1_CH0_PIN) = PORT_PCR_MUX(3);      // CH0 pin
    
    	FTM1_MODE |= FTM_MODE_INIT;                   // Initialize channel outputs
    
    	FTM1_SYNCONF = FTM_SYNCONF_SYNCMODE;              // Enhanced sync mode
    
    	FTM1_SYNCONF |= FTM_SYNCONF_HWRSTCNT;             // Select HW counter sync trigger
    	FTM1_SYNCONF |= FTM_SYNCONF_HWWRBUF;              // HW sync of MOD, CNTIN, and CV registers
    	FTM1_SYNCONF |= FTM_SYNCONF_HWTRIGMODE;
    	FTM1_SYNC = FTM_SYNC_TRIG0;
    
    	FTM1_SC = FTM_SC_TOIE;				// Enable overflow interrupt
    	FTM1_SC |= FTM_SC_PS(3);			// prescaler = 8
    	FTM1_SC |= FTM_SC_CLKS(1);			// run from sys clk
    
    	NVIC_ENABLE_IRQ(IRQ_FTM1);
    }
    Attached Thumbnails Attached Thumbnails Click image for larger version. 

Name:	w1.jpg 
Views:	8 
Size:	112.8 KB 
ID:	16070   Click image for larger version. 

Name:	w2.jpg 
Views:	6 
Size:	112.9 KB 
ID:	16071  

    Click image for larger version. 

Name:	w3.jpg 
Views:	5 
Size:	112.6 KB 
ID:	16072  

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •