Teensy 3.x locked anti phase pwm (LAP mode)

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)

Grazie Astro ! :slight_smile:

Prima che qualcuno "distratto" lo provi e si lamenti che non funziona ... faccio solo di nuovo notare che il codice NON è per Arduino, ma per per la Teensy 3.x !!!

Guglielmo

Premesso che l'argomento è ai limiti delle mie conoscenze, ci sta ogni tanto qualche topic che alzi l'asticella :slight_smile:

Premesso che il codice , per me che non sono un programmatore, è tutto da capire......
Ma sostanzialmente è per risparmiarsi una porta inverter ?

Brunello:
Ma sostanzialmente è per risparmiarsi una porta inverter ?

Non è solo per risparmiare un inverter, è sia per poter disporre del controllo sulla dead band, di vitale importanza se devi controllare un ponte H che non dispone nativamente di questo controllo, ma anche per poter utilizzare una frequenza di carrier alta, anche questo molto importante, con una risoluzione decente.
Rammento che il comando analogwrite() standard di Arduino genera un pwm a solo 480 Hz con 8 di risoluzione anche sulla Teensy 3.x, risoluzione bassa per ottenere un controllo decente di un motore e troppo bassa la frequenza per il modo LAP.
In tutti i casi non sempre è semplice inserire un inverter esterno, normalmente le schede con ponti H basati su L29x, etc., non lo integrano, mettono a disposizione solo gli ingressi IN1, IN2 e Enable per ogni Hbridge contenuto nel IC.

Oggi pomeriggio arriva una versione del codice sotto forma di libreria con incluse anche le funzioni per settare il pwm con un singolo comando.

Quasi finita la trasformazione del codice come libreria, ho aggiunto delle feature oltre al semplice init del timer, appena finito di ritestare il funzionamento la posto.
La libreria verifica il tipo di scheda usata così da evitare tentativi di compilazione per schede non compatibili, per il momento sono supportate la Teensy 3.0, 3.1, 3.2, 3.5, 3.6, la 3.5 e la 3.0 non sono testate in pratica perché non ho nel cassetto la scheda per fare i test.
Se ci riesco in giornata, altrimenti se ne parla domani, posto un breve tutorial relativo all'uso del PWM in modo LAP con i ponti H, come calcolare la frequenza di carrier ottimale e perché conviene usare questa modalità di controllo, se il ponte lo prevede, invece della modalità S/M.

Ecco la versione in formato libreria, dato che servono alcuni parametri per inizializzare il FlexTimer 0, in modo da ottenere il desiderato valore di frequenza del pwm, è necessario indicarli nel file principale dello sketch allegando le seguenti righe come prima cosa:

// PWM parameters
#define TPM_C 72000000    // FTM0 Clock
#define PWM_FREQ 50000    // PWM frequency x 2
#define PWM_DEAD_BAND 9   // Dead Band 2 µs (4.5 = 1 µs)
#define MODULO (TPM_C / PWM_FREQ)
#include "pwm_lap.h"

Nell'esempio allegato alla libreria ci sono tutte le indicazioni per l'uso della stessa sotto forma di commento iniziale.

pwm_lap.zip (3.85 KB)

Astro con cosa hai compresso la lib perché ho provato sia con utility di compressione del mac e mi genera un .cpgz, e winzip mi da errore di compressione

Ho usato winrar 5, ho dovuto rinominare il file come .zip perché il forum non accetta allegati compressi con estensione diversa da .zip.
Prova a rinominarlo in .rar, in questo modo winzip dovrebbe aprirlo senza problemi.

ok si apre rinominandolo, thx

Volendo usare il pwm in lap mode con un driver TB6612FNG ci sono suggerimenti su come fare i collegamenti (link)?

Per il canale A il driver ha 3 ingressi. AIN1, AIN2, PWMA. Come li collego ai PIN 9 e 10 della Teensy per ottenere il PWM in LAP mode?

Lo schema disponibile fa un po pena, ma cosi ad occhio ... se tieni alto il PWM ed usi i due IN, dovresti poterlo usare in LAP (considerando anche che se mandi entrambi gli IN alti il motore viene cortocircuitato, frenando di colpo, mentre se li mandi entrambi bassi, le uscite vanno entrambe aperte, in alta impedenza) ... considera che in quel chip il motore viene cortocircuitato anche se mandi basso il PWM, indipendentemente dallo stato dei due IN ... quindi potresti al massimo sfruttarlo come "freno-motore" in quelle condizioni, pilotandolo esternamente, ma e' una cosa che puoi fare comunque con i due ingressi alti entrambi, volendo, quindi se quella funzione specifica non ti serve, tienilo alto ed amen ...

Grazie, faccio qualche prova.

p.s.
il dsNAV? News?

Ottimo lavoro, peccato che ho già fatto stampare il il PCB e le uscite PWM 22 e 23 le uso x altro.
Sarebbe possibile con qualche istruzione nel codice cambiarle in 9 e 10?
Grazie

>Valter1966: essendo il tuo primo post, nel rispetto del regolamento (… punto 13, primo capoverso), ti chiedo di presentarti QUI (spiegando bene quali conoscenze hai di elettronica e di programmazione ... possibilmente evitando di scrivere solo una riga di saluto) e di leggere con MOLTA attenzione il su citato REGOLAMENTO ... Grazie.

Guglielmo

OK scusate
Presentazione fatta

Valter1966:
Sarebbe possibile con qualche istruzione nel codice cambiarle in 9 e 10?

Pin 9 e 10 sono già utilizzati per il canale PWM A, 22 e 23 sono per il canale PWM B e sono comunque obbligatori perché imposti dalla configurazione hardware del Flexy Timer FTM0.

Scusate, ma ho indicato i piedini sbagliati :confused:

Io ho i 2 motori PWM collegati ai piedini 9-10 e 5-6 del Teensy3.2

9-10 OK
5-6 posso usarli al posto di 22 e 23 (su 22 e 23 ho dei sensori IR analogici)