In allegato un codice di esempio per attivare sulla Teensy 3.x due canali pwm in modalità LAP, due segnali tra loro in anti fase, viene utilizzato il Flexy Timer FTM0 assieme ai pin 9-10, PWM CHA, e 22-23, PWM CHB, è possibile utilizzare frequenze PWM abbastanza alte mantenendo una risoluzione migliore di 10 bit, più che sufficiente per la stragrande maggioranza delle applicazioni.
L'esempio attiva i due canali PWM con un carrier di 25 kHz e una risoluzione di 1440 count, 720 = 50%, il codice è ampiamente commentato, basta chiamare dalla setup la funzione PWMPhaseShift() per inizializzare il timer FTM0, dopo di che è sufficiente modificare il valore di due registri per modificare il duty cycle.
La funzione PWMPhaseShift() provvede anche a inizializzare i pin utilizzati per il PWM come out, pertanto non è necessario settarli preventivamente.
L'utilizzo dei canali PWM in modalità LAP è di vitale importanza quando si utilizzano ponti H della serie L29x, L620x, in generale ponti che prevedono i due ingressi separati per il controllo dei rami, perché non possono essere utilizzati in modalità S/M (Sign/Magnitude).
La prossima settimana arriva il codice per usare il Flexi Timer FTM1 come gestore hardware di due encoder incrementali in quadratura, è possibile ottenere sia la distanza (numero di count) che la velocità di rotazione.
Il tutto verrà unito in un codice più ampio che implementa una smart motion controller, ad uso robot e/o sistemi di movimentazione, che rilascerò nell'immediato futuro.
edit:
Il codice è stato testato sulla Teensy 3.1, 3.2 e 3.6, sicuramente funziona anche sulla obsoleta 3.0 e la recente 3.5 però non posso garantirlo in quanto non l'ho testato su queste due schede.
edit 2:
Inserita la versione in formato libreria, qui i dettagli.
/*
Dual Phase correct PWM Teensy 3.x
flextimer FTM0
PWM CHA pin 9-10
PWM CHB pin 22-23
*/
/*
Per aggiornare il duty cycle scrivere il valore di count nei registri FTM0_C1V e FTM0_C3V
poi settare il registro "FTM0_SYNC |= 0x80" per attivare il trigger per la sincronizzazione.
Il valore del modulo è il numero di count per il ciclo del timer, al tempo stesso è anche
il range di azione del PWM, p.e. se il modulo è 1500 il PWM al 50% è pari a 750.
Il modulo dipende dalla frequenza desiderata per il carriere del pwm e dalla frequenza
di clock del timer, p.e. con clock a 72 MHz (Teensy 3.1) per una frequenza di 25 kHz il
il modulo vale 1440
*/
const int ledPin = 13;
const int PWMA_H = 9; // FTM0 - CH0
const int PWMA_L = 10; // FTM0 - CH1
const int PWMB_H = 22; // FTM0 - CH2
const int PWMB_L = 23; // FTM0 - CH3
#define TPM_C 72000000 // core clock
#define PWM_FREQ 50000 // PWM frequency * 2
#define MODULO (TPM_C / PWM_FREQ) // modulo for FTM0
int PWMAvalue; // Duty Cycle CHA
int PWMBvalue; // Duty Cycle CHB
boolean led_T; // Stato led
int PWMvalue =0;
bool countDir =1;
void setup() {
pinMode(ledPin, OUTPUT);
PWMPhaseShift(); // setup Flexi Timer FTM0
Serial.begin(115200); // solo per debug
}
void loop()
{
// loop valori pwm ascendenti e discendenti
if(PWMvalue<MODULO && countDir)PWMvalue+=50;
else if(countDir)countDir =0;
if(PWMvalue>200 && !countDir)PWMvalue-=50;
else if(!countDir)countDir =1;
// assegna valori pwm CHA e CHB
PWMAvalue = PWMvalue; ;
PWMBvalue = PWMvalue;
// set duty cycle e trigger
FTM0_C1V = PWMAvalue; // PWM Ch A Duty Cycle
FTM0_C3V = PWMBvalue; // PWM Ch B Duty Cycle
FTM0_SYNC |= 0x80; // set PWM value update
// lampeggio led
led_T ^= 1;
digitalWrite(ledPin, led_T); // toggle led
delay(100); // led refresh period
}
// setup Flexy Timer FTM=
void PWMPhaseShift(void)
{
FTM0_OUTMASK = 0xFF; // Use mask to disable outputs
// init pwm pin 9-10-22-23
CORE_PIN9_CONFIG = PORT_PCR_MUX(4) | PORT_PCR_DSE | PORT_PCR_SRE;
CORE_PIN10_CONFIG = PORT_PCR_MUX(4) | PORT_PCR_DSE | PORT_PCR_SRE;
CORE_PIN22_CONFIG = PORT_PCR_MUX(4) | PORT_PCR_DSE | PORT_PCR_SRE;
CORE_PIN23_CONFIG = PORT_PCR_MUX(4) | PORT_PCR_DSE | PORT_PCR_SRE;
SIM_SCGC6 |= 0x03000000; // enable FTM0 and FTM0 module clock
SIM_SCGC5 = SIM_SCGC5 | 0x3E00; // enable port A/B/C/D/E clock
FTM0_SC = 0x00; // FTM0 disabled
FTM0_MOD = MODULO; // Period register
FTM0_CONF = 0xC0; // set up BDM in 11
FTM0_FMS = 0x00; // clear the WPEN so that WPDIS is set in FTM0_MODE reg
FTM0_MODE |= 0x05; // enable write the FTM CnV register
FTM0_SYNC = 0x02; // PWM sync @ max loading point enable
FTM0_C0SC = 0x28; // High-Low_high for combined and complementary mode
FTM0_C1SC = 0x28;
FTM0_C2SC = 0x28;
FTM0_C3SC = 0x28;
// complementary and combined mode for CH0&CH1, CH2&CH3
// dead timer and sync enabled for CH0&CH1, CH2&CH3
FTM0_COMBINE = 0x00003333;
//FTM0_DEADTIME = 0x0F; // deadtime is 15 system clock cycles
FTM0_DEADTIME = 0x80; // DeadTimer prescale systemClk/4
FTM0_DEADTIME |= 12; // 1uS DeadTime, max of 63 counts of 48Mhz clock
FTM0_C0V = 0;
FTM0_C1V = MODULO/2; // Duty Cycle 50%
FTM0_C2V = 0;
FTM0_C3V = MODULO/2;
FTM0_CNTIN = 0x00;
FTM0_SC = 0x08; // PWM edge_alignment, system clock driving, dividing by 1
FTM0_OUTMASK = 0x00; // Use mask to enable outputs
}
pwm_lap.zip (3.85 KB)