Hi,
I have a master - slave configuration with three FRDM boards (1 Master | 2 Slaves).
The slave SPWM is synchronized to the master using 2 signals i.e. a 160kHz SPWM and 16kHz Clock signal. (Sync between multiple FRDM K64F boards for PWM gen... - NXP Community , Re: FTM0_CH6 (160 kHz) sync with FTM2_CH0 (16 kHz)... - NXP Community)
As mentioned in the other posts, there is a state machine in this project to put the system into particular states and a trigger button is used to turn ON and OFF the SPWM outputs.
The problem is that sometimes when the trigger button is pressed one of the slave output goes flat high ( no SPWM, just logic HIGH). After this if the trigger button is pressed again to turn OFF, the output comes to flat low (ZERO) and then further one more press would get the output back to normal SPWM. This occurrence is random, there is no pattern observed.
Below are the PWM related functions init, run and stop.
bool initPWM(outputWaveShape shape, uint32_t fOutput, uint32_t fClock, uint32_t deadTime)
{
uint32_t FTM0ModVal = 0;
uint32_t dtVal = 0;
switch(shape)
{
case sine:
FTM0ModVal = 1 / ((1 / (float) fClock) / (1 / (2 * (float) fOutput * (float) BUFF_LENGTH)));
break;
}
/**********************************************************************************
* PORT SETUP
* ********************************************************************************
*/
// System Clock Gating Control Register 5 (SIM_SCGC5), switch on clock for port A
*(volatile unsigned long *)(SIM_BLOCK + SIM_PORT_CLK_GATE_OFFSET) |= 0x00000200;
// FTM0_CH6 on PTA1 (alt. function 3) and high drive strength
*(volatile unsigned long *)(PORTA_BLOCK + 0x04) |= (0x00000300 | 0x00000040);
// FTM0_CH7 on PTA2 (alt. function 3)
*(volatile unsigned long *)(PORTA_BLOCK + 0x08) |= (0x00000300 | 0x00000040);
/**********************************************************************************
* Timer Channel n Setup
* ********************************************************************************
*/
// FTM. Ensure that the FlexTimer/TPM module is powered up
*(volatile unsigned long *)(SIM_BLOCK + 0x103c) |= 0x01000000;
// FTM Status And Control (FTM0_SC). Disable FTM0 operation (system clock)
*(volatile unsigned long *)(FTM0_BASE_ADDR + 0x000) = 0x00000000;
/* Configuration (FTMx_CONF) pp 1020
* pin
* 6 - 7 - BDM mode 3 - 0xc0 (allow timer to continue operating when debugging). BDM mode 1 - 0x40
* 9 - Enable use of global timebase from another FTM module 0x200 - not here this is the module that will act as master
* 10 - Enable output of global timebase to other FTMs 0x400 - not yet though. use this to start FTM0 and FTM1 synchronously
*
* For debugging the Flextimer without driving it from the DMA, 0x40 is useful. For debugging the control loop 0xc0 is better
* because it allows the switching to keep going even if the control loop is being interrogated.
*/
//*(unsigned long *)(FTM0_BASE_ADDR + 0x084) |= (0x00000040);
*(unsigned long *)(FTM0_BASE_ADDR + 0x084) = (0x000002c0);
// Disable Write protection on the FTMx_MODE.
*(unsigned long *)(FTM0_BASE_ADDR + 0x054) = 0x00000004;
// Features Mode Selection (FTMx_MODE)
// Switch on the enhanced FTM features - 0x01
// Keep write protection disabled - 0x04
*(unsigned long *)(FTM0_BASE_ADDR + 0x054) = (0x00000004 | 0x00000001);
// Synchronisation (FTMx_SYNC).
// Enable loading at maximum (i.e. CV == MOD) - 0x00000002
// Software triggering is enabled - 0x80
// *(unsigned long *)(FTM0_BASE_ADDR + 0x58) = (0x00000002 | 0x80);
*(unsigned long *)(FTM0_BASE_ADDR + 0x58) = (0x00000004 | 0x40);
// Set counter MOD register to the value that provides the frequency of interest.
// 0xBB0; - 20 kHz
// 0x6B2; - 35 kHz
// 0x3C8; - 62 kHz
// 0x0BB; - 320 kHz
*(volatile unsigned long *)(FTM0_BASE_ADDR + FTM0_MOD) = (FTM0ModVal - 1);
// A counter
int i = 0;
// Pointer to the address of the FTM Modulo register.
volatile unsigned long *mod;
mod = (volatile unsigned long *)(FTM0_BASE_ADDR + FTM0_MOD);
// write the data that will be passed by the DMA to the FTM CnV registers.
for(i = 0 ; i<BUFF_LENGTH; i++)
{
PWMAData[i] = 0;
switch(shape)
{
case sine:
// prevent large voltage output when timer starts...
s->tempmaxDutyPercent = 0;
ModulatedSineDutyValues[i] = (( (int16_t) dutyCyclePercentSine[i] - 50) * s->tempmaxDutyPercent)/100 + 50;
PWMBData[i] = (*mod * (ModulatedSineDutyValues[i]) / 100) + PWMAData[i];
break;
}
/* Synchronisation (FTMx_SYNC) register DMA memory buffer.
* 0x80 is the software sync flag. The hardware clears this after every sync.
* 0x02 is the flag that sets the synchronisation of registers with buffers to happen when CV == MOD.
* this only happens if the software sync flag has been raised prior to CV == MOD occurring.
* 0x08 is the output mask enable bit.
*/
SyncData[i] = (0x80 | 0x02 | 0x08);
}
/* Synchronisation Configuration (FTMx_SYNCONF)
* set SYNCMODE (Enhanced PWM synchronisation is selected.) - 0x80
* set SWRSTCNT = 1 (FTM counter synchronisation is activated by the software trigger) - 0x100
* set SWWRBUF = 1 (MOD, CNTIN, and CV registers synchronisation is activated by the software trigger.) - 0x200
* set SWOM = 1 (Software Output Mask register is updated from its buffer by the software trigger) - 0x400
*/
// *(unsigned long *)(FTM0_BASE_ADDR + 0x8C) = (0x200 | 0x80 |0x400);
// *(unsigned long *)(FTM0_BASE_ADDR + 0x8C) = (0x80 |0x60000);
*(unsigned long *)(FTM0_BASE_ADDR + 0x8C) = (0x30001 | 0x04 | 0x80);
// Channel (n) Status And Control (FTMx_CnSC)
// DMA = 1 - 0x01
// ELSnB:ELSnA = 1:0 Edge-Aligned PWM High-true pulses (clear Output on match) - 0x08
// MSnB:MSnA = 1:0 (See ELSnB:ELSnA) - 0x20
// CHIE = 1 Enable Channel interrupts (necessary for DMA to be fired but when DMA = 1 the interrupt itself is not generated) - 0x40
//*(volatile unsigned long *)(FTM0_BASE_ADDR + FTM0_C6SC_OFFSET) = (0x00000020 | 0x00000008 | 0x00000001 | 0x00000040);
*(volatile unsigned long *)(FTM0_BASE_ADDR + FTM0_C6SC_OFFSET) = (0x00000010 | 0x00000008 | 0x00000001 | 0x00000040);
// Set counter initial value to zero
*(volatile unsigned long *)(FTM0_BASE_ADDR + 0x04c) = 0;
// prepare first PWM value (place it in the CnV register)
*(volatile unsigned long *)(FTM0_BASE_ADDR + FTM0_C6V_OFFSET) = *ptrPWMABuffer;
// prepare output mask for running the inverter
*(volatile unsigned long *)(FTM0_BASE_ADDR + 0x60) = (0xC0);
/**********************************************************************************
* Timer Channel n+1 Setup
* ********************************************************************************
*/
// Channel (n) Status And Control (FTMx_CnSC)
// DMA = 1 - 0x01
// ELSnB:ELSnA = 1:0 Edge-Aligned PWM High-true pulses (clear Output on match) - 0x08
// MSnB:MSnA = 1:0 (See ELSnB:ELSnA) - 0x20
// CHIE = 1 Enable Channel interrupts (necessary for DMA to be fired but when DMA = 1 the interrupt itself is not generated) - 0x40
//*(volatile unsigned long *)(FTM0_BASE_ADDR + FTM0_C7SC_OFFSET) = (0x00000020 | 0x00000008 | 0x00000001 | 0x00000040);
*(volatile unsigned long *)(FTM0_BASE_ADDR + FTM0_C7SC_OFFSET) = (0x00000010 | 0x00000008 | 0x00000001 | 0x00000040);
// Function For Linked Channels (FTMx_COMBINE).
// Enable combined mode on CH6 and CH7 bit 24 - 0x1000000
// Enable complimentary mode on CH6 and CH7 bit 25 - 0x2000000
// Enable Deadtime on CH6 and CH7 bit 28 - 0x10000000
// Enable PWM Synchronisation between CH6 and CH7 bit 29 - 0x20000000
*(unsigned long *)(FTM0_BASE_ADDR + 0x64) = (0x1000000 | 0x2000000 | 0x20000000 | 0x10000000);
// Deadtime Insertion Control (FTMx_DEADTIME) 80 is pre-scaler of 4 0x3C is 60
//(0x1E | 0x80) - 2us
// 0x3C - 1us
// The bus clock used for this which is (system clock / 2) (60MHz in the FRDM case). The final value is in clock ticks, deadTime is in ns.
// if no prescaler is required then the deadtime is calculated using the first 6 bits of the dead time register.
// If a value more than 0x40 * the divided system clock is needed then the pre-scaler must be used.
// Bits 6 and 7 are responsible for a prescaler of:
// 6,7
// 0x - 1
// 10 - 4
// 11 - 16
// 10e6 translate to 10^7, the below equation translate into
// (Fsys/2)*(deadtime in nS/10^9)
dtVal = (uint32_t)(deadTime * ( (float) fClock / 10e6) / 200 );
if (dtVal > 0x3F)
{
// The deadtime is more than the maximum without a pre-scaler
if ((dtVal / 4) < 0x40 )
{
// The deadtime is less than the maximum with a pre-scaler of 4 so set the pre-scaler to 4
*(volatile unsigned long *)(FTM0_BASE_ADDR + 0x68) |= 0x80;
dtVal = dtVal / 4;
*(volatile unsigned long *)(FTM0_BASE_ADDR + 0x68) |= dtVal;
}
else
{
if((dtVal / 16) < 0x40)
{
// The dead-time is less than the maximum with a prescaler of 16 so set the pre-scaler to 16.
*(volatile unsigned long *)(FTM0_BASE_ADDR + 0x68) |= 0xC0;
dtVal = dtVal / 16;
*(volatile unsigned long *)(FTM0_BASE_ADDR + 0x68) |= dtVal;
}
else
{
// The dead-time is more than the maximum value even with a pre-scaler of 16, or something else is wrong.
// return an error.
return true;
}
}
}
else
{
*(volatile unsigned long *)(FTM0_BASE_ADDR + 0x68) |= dtVal;
}
// prepare first PWM value (place it in the CnV register)
*(volatile unsigned long *)(FTM0_BASE_ADDR + FTM0_C7V_OFFSET) = *ptrPWMBBuffer;
return false;
}
void stopPWM()
{
/**********************************************************************************
* Disable Clock to FTM module.
* ********************************************************************************
*/
PWMGenerationisRunning = false;
// de-activate the global timebase output for FTM0. This stops FTM0 and FTM1 which in turn
// stops the PDB and therefore the ADC conversion.
if(getPWMStatus())
{
/* Synchronisation Configuration (FTMx_SYNCONF)
* set SYNCMODE (Enhanced PWM synchronisation is selected.) - 0x80
* set SWRSTCNT = 1 (FTM counter synchronisation is activated by the software trigger) - 0x100
* set SWWRBUF = 1 (MOD, CNTIN, and CV registers synchronisation is activated by the software trigger.) - 0x200
* set SWOM = 1 (Software Output Mask register is updated from its buffer by the software trigger) - 0x400
*
* Key thing here is that output masking is enabled. No reason not to do this in the main FTM setup if we wanted to integrated it there
*/
//*(unsigned long *)(FTM0_BASE_ADDR + 0x8C) = (0x200 | 0x80 |0x400);
*(unsigned long *)(FTM0_BASE_ADDR + 0x8C) = (0x80 |0x60000);
// prepare output mask to stop CH6 anc CH7 propagating. States will be held low.
//*(volatile unsigned long *)(FTM0_BASE_ADDR + 0x60) = (0x40 | 0x80);
/* Synchronisation (FTMx_SYNC) register.
* 0x80 - is the software sync flag. The hardware clears this after every sync.
* 0x02 - is the flag that sets the synchronisation of registers with buffers to happen when CV == MOD
* this only happens if the software synch flag has been raised prior to CV == MOD occurring.
* 0x08 - SYNCHOM - sets when the output mask buffer is written to the register 0 is at every clock event, 1 is via the PWM synchronisation
*/
// prepare the sync including output mask.
//*(volatile unsigned long *)((FTM0_BASE_ADDR + 0x58)) = (0x08 | 0x02);
*(volatile unsigned long *)((FTM0_BASE_ADDR + 0x58)) = (0x08 | 0x04);
// do the sync
*(volatile unsigned long *)((FTM0_BASE_ADDR + 0x58)) = (0x40);
*(volatile unsigned long *)(FTM0_BASE_ADDR + 0x60) = (0xC0);
// power down the module.
*(unsigned long *)(FTM0_BASE_ADDR + 0x084) = (0x000000c0);
*(volatile unsigned long *)(FTM0_BASE_ADDR + 0x000) = 0x00000000;
}
}
Please suggest what could be the causes for such random behavior of PWM channels
Thanks & Regards,
Rashmitha