Caricabatteria USB con ATmega

Una connessione USB, un microcontrollore ATMega ed un convertitore DC/DC sono tutto quello che serve per realizzare un caricabatterie per celle a ioni di litio. Scopriamo come.

Nel 2008 Atmel ha introdotto la nuova famiglia di microcontrollori ATmega16/32U4 che combinano interfaccia USB 2.0 e funzionalità analogiche ad elevate prestazioni. Grazie a queste caratteristiche, tali microcontrollori rappresentano una soluzione ideale per sistemi a basso costo e con alimentazione da porta USB per applicazioni come accessori per console di gioco (ad esempio gamepad) o caricabatterie. E proprio di ciò, un caricabatterie per celle a ioni di litio, parleremo in questo articolo.

Il problema della carica delle batterie

Prima di presentare i dettagli dell’applicazione, può essere utile introdurre brevemente il problema della carica delle batterie a ioni di litio. Le batterie agli ioni di litio sono la tecnologia oggi più diffusa per le celle ricaricabili. Tra i vantaggi principali vi sono una bassa resistenza interna, un tempo di vita elevato, tempi di carica inferiori, l’assenza di un effetto memoria della cella, una più bassa auto-scarica, minore tossicità. Lo svantaggio principale è invece legato alla scarsa tolleranza dei dispositivi a sovracarica; in questo caso potrebbero verificarsi elevati assorbimenti di corrente ed un rapido incremento della temperatura interna del dispositivo che potrebbe andare in fuga termica. Non sono rari i casi segnalati di batterie ricaricabili utilizzate in apparecchiature consumer (computer portatili, ad esempio) che sono esplose, provocando non pochi danni, proprio per problemi al circuito di carica. La procedura di carica di una batteria agli ioni di litio prevede tre diverse fasi, come mostrato schematicamente in figura 1. Le prime due sono a corrente costante mentre la terza a tensione costante. Il passaggio tra le prime due fasi è dettato dal raggiungimento di soglie per la tensione della cella. Il  processo di carica termina invece quando, nell’ultima fase di carica a tensione costante, la corrente assorbita, che decresce nel tempo, scende al di sotto di una soglia. I valori delle soglie e delle correnti di carica da applicare per le diverse fasi sono definiti dal costruttore e specifici della particolare serie di batterie;  i valori in figura 1 sono da riferirsi ad una batteria della seria VARTA™ EasyPack da 550mAh.

Figura 1: la procedura di carica di una celle a ioni di litio (da [1]).

Figura 1: la procedura di carica di una
celle a ioni di litio (da [1]).

Il cuore del circuito:  l’ATmega

Per realizzare il nostro circuito caricabatteria, tutto quello che ci occorre sono quindi una tensione di alimentazione in ingresso (ad esempio a 5V), un circuito di conversione DC/DC per generare la tensione di carica della batteria ed un microcontrollore per controllare  il processo di carica secondo lo schema indicato in precedenza. Tutte queste risorse sono disponibili sulla scheda di sviluppo EVK527 mostrata in figura 2.

Figura 2: la scheda di sviluppo EVK527 per ATmega32U4.

Figura 2: la scheda di sviluppo EVK527 per ATmega32U4.

La scheda, infatti, si basa su un micro ATmega32U4 con 32 KB di memoria Flash, porta USB 2.0 e ADC a 12 canali e dispone di un circuito di conversione DC/DC con topologia buck e controllo di tipo PWM generato direttamente dal microcontrollore. La porta USB può essere usata sia per derivare l’alimentazione per la scheda ed il circuito di carica della batteria che come linea di comunicazione con un terminale remoto. L’ATmega32U4 è un microcontrollore con architettura RISC con capacità di calcolo fino a 16 MIPS; include un register file di 32 x 8 locazioni ed un moltiplicatore embedded in grado di eseguire un’operazione in 2 cicli di clock. Include 32KByte di memoria flash in-system selfprogrammable, 2.5 KByte di memoria SRAM ed EEPROM di dimensioni 1 KByte. Dispone, come detto, di una porta USB 2.0 in grado di sostenere data rate in trasferimento fino a 12 Mbps ed di un convertitore ADC a 12 canali e 10-bit di risoluzione. Integra un circuito PLL, timer e counter ad 8 e 16 bit, diversi canali PWM anche per applicazioni high-speed, interfaccia SPI master/slave e porta USART programmabile con controllo di flusso hardware. Supporta tensioni di alimentazione tra 2.7 V e 5.5V; la corrente assorbita, ad una frequenza di clock di 4 MHz, è di solo 2.2 mA, rendendo così ideale il dispositivo  per applicazioni con alimentazione da porta USB.  Questa stessa funzionalità è usata nel caso del nostro caricabatteria per stabilire un canale di comunicazione tra la scheda di sviluppo EVK527 ed un terminale remoto per l’invio di informazioni relative allo stato del processo di carica. Il convertitore ADC dell’ATmega è invece usato per controllare costantemente lo stato di carica della batteria.

Il circuito di conversione DC/DC

Come accennato in precedenza, per generare la tensione di carica della batteria occorre un circuito di conversione della tensione DC/DC. La scheda utilizza un convertitore buck il cui schema di principio è mostrato in figura 3.

Figura 3: schema di principio di un convertitore buck

Figura 3: schema di principio di un convertitore buck

I convertitori buck sono convertitori in continua di tipo step-down caratterizzati da elevata efficienza (tipicamente maggiore del 95%). Il principio di funzionamento su cui si basano è piuttosto semplice; un induttore viene caricato e scaricato alter nativamente attraverso un diodo ed uno switch (tipicamente un transistor) per trasferire energia dall’ingresso verso il carico. Se la corrente attraverso l’induttore non si riduce mai a zero, il convertitore  opera in modalità continua seconda lo schema temporale riportato in figura 4a. In questo caso, quando lo switch è chiuso il diodo è contro polarizzato; quindi non conduce. L’induttore assorbe energia dall’ingresso e la corrente aumenta linearmente. Quanto lo switch viene aperto, il diodo viene forzato in conduzione e l’induttore eroga l’energia immagazzinata; l’intensità della corrente decresce linearmente nel tempo. Il rapporto tra la tensione di ingresso e quella di uscita è dato dal duty cycle del periodo di commutazione dello switch. Se invece l’induttore si scarica completamente durante l’intervallo di off dello switch, il convertitore opera in modalità discontinua (figura 4b); la tensione di uscita in questo caso è funzione anche del valore dell’induttore, del periodo di commutazione dello switch e della corrente assorbita dal carico. Nel nostro caso, lo switch del convertitore buck viene controllato direttamente dal microcontrollore sfruttando una delle uscite PWM disponibili a bordo dell’ATmega; in questo modo sarà possibile controllare in maniera precisa il duty-cycle del periodo di commutazione e quindi, per quanto osservato in precedenza, la tensione di uscita del circuito. Il clock per il circuito PWM viene generato internamente al microcontrollore mediante PLL e post-scaler; la frequenza impostata è la massima consentita di 64 MHz, così da ottenere la massima risoluzione possibile nella modulazione del duty-cycle. Il segnale  PWM di controllo risultante ha un periodo 250 KHz.

Figura 4: principio di funzionamento di un convertitore buck nelle modalità continua (a) e discontinua (b) (da wikipedia).

Figura 4: principio di funzionamento di un convertitore buck nelle modalità continua (a) e discontinua (b) (da wikipedia).

Il software applicativo

Prima di usare la scheda di sviluppo per applicazioni proprie, tenere presente che l’esempio è stato sviluppato per una particolare batteria Varta; nel caso quindi di batterie diverse andranno modificati opportuni parametri del software onde evitare di danneggiare  il nostro dispositivo. Il listato 1 riporta la funzione main() dell’applicativo che è stato sviluppato per gestire il caricabatteria.

int main(void)
{
   Usb_enable_regulator();
   wdtdrv_disable();
   start_boot_if_required();
   Clear_prescaler();
   scheduler();
   return 0;
}
Listato 1

Come si vede è piuttosto semplice e schematica. La funzione Usb_enable_regulator() abilita il regolatore  interno per i pads USB; tipicamente viene disabilitato per applicazioni con tensioni di alimentazione inferiore a 3.5V per ottimizzare la dissipazione di potenza. Quindi, la funzione wdtdrv_disable()  disabilita il watchdog timer del microcontrollore. In seguito viene avviato il boot-loader - start_boot_if_required() – e resettato il prescaler del clock interno della CPU – clear_prescaler(). Infine viene attivato lo scheduler(). Tale scheduler è un loop infinito che richiama in sequenza  i task che sono stati abilitati all’inter no del file conf_scheduler.h; non è implementato nessuno schema di pianificazione in tempo reale ma, semplicemente, al termine di un task è chiamato quello immediatamente successivo nella sequenza predefinita.  Il ciclo infinito è preceduto dalla inizializzazione  dei diversi task previsti mediante chiamata delle apposite funzioni relative. Nella nostra particolare applicazione sono previsti i  seguenti tre task : usb_task(), cdc_task() e batt_task(). I  primi due si riferiscono alla gestione della porta seriale e della comunicazione con il terminale remoto. Il  terzo invece controlla il processo di carica della batteria. Il cuore della funzione è una macchina a stati implementata secondo il diagramma di flusso riportato in figura 5.

Figura 5: diagramma di flusso della macchina a stati della funzione batt_task().

Figura 5: diagramma di flusso della macchina a stati della funzione batt_task().

Il listato 2 evidenzia la parte di codice corrispondente all’inter no del file sorgente batt_task.c. Ad ogni stato, come si vede, è associato un puntatore a funzione che viene usato per chiamare la funzione appropriata all’inizio del ciclo. Ognuna delle funzioni ritorna a sua volta il prossimo stato. Il ciclo inizia con la chiamata alla funzione ADC_Wait() che consente di aggiornare  i parametri dello stato di carica della batteria; questa stessa funzione viene anche chiamata ripetutamente all’interno delle altre funzioni di stato per verificare come proseguire nel processo.  La funzione charge(), in particolare,  gestisce il processo di carica nelle diverse fasi di pre-qualifica, carica a corrente costante e carica a tensione costante in accordo allo schema discusso inizialmente.

unsigned char CurrentState; //!< \brief Global that indicates current state
//!<
//!< Updated by main().
//!< \note See menu.h for definition of states.
unsigned char nextstate, inp;
unsigned char (*pStateFunc)(unsigned char); // Function pointer.
…
//wait a complete ADC conversion to update batt measurements
ADC_Wait();
// Run function associated with current state, get next state in return.
if (pStateFunc != NULL){
nextstate = pStateFunc(inp);
}
// Look up function for next state, if it differs from the current.
if (nextstate != CurrentState) {
CurrentState = nextstate;
for ( i = 0; menu_state[i].state != 0; i++) {
if (menu_state[i].state == CurrentState) {
pStateFunc = menu_state[i].pFunc;
}
}
}
Listato 2

 

 

2 Commenti

  1. Giovanni Di Maria Giovanni Di Maria 2 gennaio 2019
  2. Stefano Lovati Stefano Lovati 3 gennaio 2019

Scrivi un commento