PIC12F675

PIC12F675 CharliePlexing con 6 Led

CharliePlexing

Una delle difficoltà che possono incontrare i progettisti elettronici è avere a disposizione poco spazio, PIC piccoli, come il PIC12F675 con poche porte e dover sfruttare al massimo le potenzialità del microcontrollore: bisogna quindi procedere utilizzando sistemi più o meno complessi e in questo caso vediamo ad esempio il metodo CharliePlexing che permette di gestire più led utilizzando poche porte.

Pic12F675 CharliePlexing 6 led

Il sistema CharliePlexing sfrutta una delle principali caratteristiche dei led: essendo dei diodi emettitori di luce,  impediscono il passaggio di corrente in una direzione, mentre la consentono nell’altro!

Schema Led

Quando c’è corrente che passa da anodo a catodo, il diodo led si accende, mentre al contrario questa viene bloccata e il led non si accende.
Il CharliePlexing utilizza questa caratteristica e vediamo un esempio banalissimo e semplice del funzionamento con 2 Led.
charlieplexing 2 led

Se sul pin1  colleghiamo il positivo e sul pin2 colleghiamo il negativo il LED1 si accenderà, mentre il LED2 resterà spento perchè il diodo bloccherà la corrente e non avremo corto circuiti!
Inversamente, applicando il positivo al pin2 e il negativo al pin1, si accenderà il LED2 e il LED1 funzionerà solo come diodo, bloccando il passaggio di corrente!
Ora vediamo cosa succede se invece di usare 2 pin, ne usiamo 3!

3 Pin Charlieplexing 6 led

Come vediamo dalla figura qui sopra, usando 3 pin, potremo inserire nel circuito 6 led .
In questo caso però oltre ad avere un pin con polo positivo ed uno a polo negativo, il terzo dovrà essere “isolato”, ad alta impedenza. In definitiva per pilotare i LED, le porte del nostro PIC dovranno essere impostate una a livello logico 1, una a livello logico 0 e l’altra in ingresso (X – alta impedenza).

LED1 LED2 LED3 LED4 LED5 LED6
pin1 1 0 X X 0 1
pin2 0 1 1 0 X X
pin3 X X 0 1 1 0

Naturalmente se avessimo a disposizione più pin potremmo collegare ulteriori led e ad esempio con 4 pin 12 led, con 5 pin 20 led e con 6 pin 30 led.

Prepariamo l’hardware

Per questo progetto occorrono:

Schema Pic12F675 charlieplexing

Per la programmazione potete utilizzare il PICKIT3 della Microchip e nel link che trovate appena sopra c’è la presentazione con gli schemi per collegarlo al micro controllore per caricare il software.

 

 Il programma

Arrivati a questo punto, dovremo preparare il programma da caricare sul PIC, bisognerà impostare le porte GPIO a livello 0 o 1 e  il rispettivo registro TRISIO  a 0 in uscita o 1 in ingresso.

Da autodidatta, cerco sempre di seguire percorsi già tracciati e quindi ben vengano le “template” o modelli già messi a disposizione dalla suite della Microchip MPLAB X IDE e quindi creando un nuovo progetto, seguendo il wizard a disposizione seleziono il modello per i PICF12 e come compilatore il XC8.
Visto che lo scopo principale di questo progettino è gestire i 6 led con la tecnica del charlieplexing ho deciso di non fare cose troppo complicate, ma nella stesura del programma ho deciso di suddividere il codice seguendo il “template” e quindi ho inserito nel file “sistem.h” le definizioni e le configurazioni.

#pragma config MCLRE = OFF, CP = OFF, CPD = ON, BOREN = ON, WDTE = OFF
#pragma config PWRTE = ON, FOSC = INTRCIO
#define _XTAL_FREQ  4000000

// definizione porte
#define MCLR	GPIO3
#define BUTTONS GPIO0
#define GPLED0	GPIO4
#define GPLED1	GPIO1
#define GPLED2	GPIO2

//definizione dei registri TRIS per il controllo LED
#define TRISGPLED0	TRISIO4
#define TRISGPLED1	TRISIO1
#define TRISGPLED2	TRISIO2

// Definizione e mappatura dei colori dei LED
#define OFF 0
#define YELLOW1	1
#define YELLOW2	2
#define RED1	3
#define RED2	4
#define GREEN1	5
#define GREEN2	6

int ledColor;
int tempo;

 Nel codice ho settato i parametri del PIC, ho definito la frequenza e per comodità ho ridefinito i nomi delle porte e i loro registri. Ad esempio la porta GPIO2 ha come alias GPLED2, ho mappato i colori dei led con un numero e ho dichiarato le variabili “ledColor” utilizzata per identificare i led e “tempo” utilizzata per temporizzare gli eventi. Sono restio ad usare i Delay() e anche se in un semplice programma come questo potevano essere una soluzione comoda, preferisco gestire i lampeggi dei led con un temporizzatore in modo tale che il microcontrollore non abbia blocchi. Per la gestione dei Led e le impostazioni delle porte e dei registri ho creato un file chiamato “charlieplexing.c”.

#if defined(__XC)
 #include <xc.h> /* XC8 General Include File */
#elif defined(HI_TECH_C)
 #include <htc.h> /* HiTech General Include File */
#endif

#include <stdint.h> /* For uint8_t definition */
#include <stdbool.h> /* For true/false definition */

#include "system.h" /* System funct/params, like osc/peripheral config */
#include "user.h"
#include "CharliePlexing.h"

//*****************************************************************************/
// Gestione LED
//
//*****************************************************************************/
void led(int ledNumber)
{
 /* Imposto le porte a 0 in modo che tutti i led siano spenti
 * Per evitare eventuali corti nel cambio si resetta sempre tutto a 0 */
 // 0 = all off

 TRISGPLED0 = 0;
 TRISGPLED1 = 0;
 TRISGPLED2 = 0;

 GPLED0 = 0;
 GPLED1 = 0;
 GPLED2 = 0;

 /* In base al valore di ledColor, passato con la funzione "led()"
 setto le porte in input o output e porto le porte in High o Low */

 switch (ledNumber)
 {
 case OFF: TRISGPLED0 = 0; //Uscita
 TRISGPLED1 = 0; //Uscita
 TRISGPLED2 = 0; //Uscita

 GPLED0 = 0; //LOW
 GPLED1 = 0; //LOW
 GPLED2 = 0; //LOW
 break;

 case YELLOW1: TRISGPLED0 = 0; //Uscita
 TRISGPLED1 = 0; //Uscita
 TRISGPLED2 = 1; //Ingresso

 GPLED0 = 0; //LOW
 GPLED1 = 1; //HIGH
 GPLED2 = 0; //LOW
 break;

 case YELLOW2: TRISGPLED0 = 0; //Uscita
 TRISGPLED1 = 1; //Ingresso
 TRISGPLED2 = 0; //Uscita

 GPLED0 = 1; //LOW
 GPLED1 = 0; //HIGH
 GPLED2 = 0; //LOW
 break;

 case RED1: TRISGPLED0 = 0; //Uscita
 TRISGPLED1 = 1; //Ingresso
 TRISGPLED2 = 0; //Uscita

 GPLED0 = 0; //LOW
 GPLED1 = 0; //LOW
 GPLED2 = 1; //HIGH
 break;

 case RED2: TRISGPLED0 = 0; //Uscita
 TRISGPLED1 = 0; //Uscita
 TRISGPLED2 = 1; //Ingresso

 GPLED0 = 1; //LOW
 GPLED1 = 0; //HIGH
 GPLED2 = 0; //LOW
 break;

 case GREEN1: TRISGPLED0 = 1; //Ingresso
 TRISGPLED1 = 0; //Uscita
 TRISGPLED2 = 0; //Uscita

 GPLED0 = 0; //LOW
 GPLED1 = 1; //HIGH
 GPLED2 = 0; //LOW
 break;

 case GREEN2: TRISGPLED0 = 1; //Ingresso
 TRISGPLED1 = 0; //Uscita
 TRISGPLED2 = 0; //Uscita

 GPLED0 = 0; //LOW
 GPLED1 = 0; //LOW
 GPLED2 = 1; //HIGH
 break;
 }
}

Come potete vedere dal codice tramite uno “switch” in base al numero del led, imposto le porte e il registro. Importante la direttiva “#include” in quanto è necessario dichiarare in quali file possono essere definite le variabili utilizzate.

Nel file “user.c” ho inserito altri parametri per la configurazione del microcontrollore e imposto la variabile TMR0 a 8 per ottenere un interrupt ogni 1ms.

#if defined(__XC)
 #include <xc.h> /* XC8 General Include File */
#elif defined(HI_TECH_C)
 #include <htc.h> /* HiTech General Include File */
#endif

#include <stdint.h> /* For uint8_t definition */
#include <stdbool.h> /* For true/false definition */

#include "user.h"

/******************************************************************************/
/* User Functions */
/******************************************************************************/

/* <Initialize variables in user.h and insert code for user algorithms.> */

void InitApp(void)
{
 TRISIO = 0b00000001; /* Imposto tutte le porte in uscita, GP0 input */
 WPU = 0b00000000; /* Set weak pull-up off on all pins */
 ANSEL = 0; /* Imposto tutte le porte come digitali */
 CMCON = 0x07; /* Disable analog comparator */

 // OPTION
// bit 0 -> PS0 Prescaler Rate Select bit 0
// bit 1 -> PS1 Prescaler Rate Select bit 1
// bit 2 -> PS2 Prescaler Rate Select bit 2
// bit 3 -> PSA Prescaler assigned to Timer0 (1=Watchdog Timer)
// bit 4 -> T0SE Timer0 Signal Edge: 0=low->high 1=high->low
// bit 5 -> T0CS Timer0 Clock Select: internal clock (1=T0CKI transition)
// bit 6 -> INTEDG INTerrupt Edge (1=raise 0=fall)
// bit 7 -> RBPU PortB PullUp (0=off 1=on)
 OPTION_REG=0b00000001;

 // INTCON
// bit 0 -> RBIF PortB Interrupt Flag
// bit 1 -> INTF RB0/INT Interrupt Flag
// bit 2 -> T0IF Timer0 Interrupt Flag
// bit 3 -> RBIE PortB Interrupt Enable (off)
// bit 4 -> INTE INT Interrupt Enable (off)
// bit 5 -> TMR0IE Timer0 Interrupt Enable (on)
// bit 6 -> PEIE PEripheral Interrupt Enable (off)
// bit 7 -> GIE Global Interrupt Enable (on)

INTCON=0b10100000;

// Preload Timer0 per ottenere 1 interrupt ogni 1ms
 TMR0=8; // corrected value
 // TMR0=6; // without correction
}

Vediamo ora il file “interrupt.c” in cui è gestito l’interrupt per il Timer0 che ho utilizzato per incrementare la variabile “tempo” ogni 1ms.

#if defined(__XC)
 #include <xc.h> /* XC8 General Include File */
#elif defined(HI_TECH_C)
 #include <htc.h> /* HiTech General Include File */
#endif

#include <stdint.h> /* For uint8_t definition */
#include <stdbool.h> /* For true/false definition */
#include "system.h" /* System funct/params, like osc/peripheral config */

/******************************************************************************/
/* Interrupt Routines */
/******************************************************************************/

/* Baseline devices don't have interrupts. Unfortunately the baseline detection
 * macro is named _PIC12 */

#ifndef _PIC12

void interrupt isr(void)
{
 /* This code stub shows general interrupt handling. Note that these
 conditional statements are not handled within 3 seperate if blocks.
 Do not use a seperate if block for each interrupt flag to avoid run
 time errors. */

/* Verifico se il timer ha il flag e se l'Interrupt è abilitato */
 /* ogni 255 viene messo il flag al timer */
 if(INTCONbits.T0IF && INTCONbits.T0IE)
 {
 INTCONbits.T0IF = 0; // elimino il flag del timer
 tempo++;   // incremento la variabile
 }

}
#endif

Infine il file principale, “main.c”, in cui un IF verifica che la variabile “tempo” sia a 500ms, mezzo secondo ed incrementa il valore della  variabile “ledColor” che setta quale led accendere. Un altro IF, fa iniziare nuovamente la sequenza di accensione dei led, settando a 1  la variabile ledColor quando questa assume il valore 7.

#if defined(__XC)
 #include <xc.h> /* XC8 General Include File */
#elif defined(HI_TECH_C)
 #include <htc.h> /* HiTech General Include File */
#endif

#include <stdint.h> /* For uint8_t definition */
#include <stdbool.h> /* For true/false definition */

#include "system.h" /* System funct/params, like osc/peripheral config */
#include "user.h" /* User funct/params, such as InitApp */
#include "CharliePlexing.h"/* headers for charliePlexing function*/

/******************************************************************************/
/* User Global Variable Declaration */
/******************************************************************************/

/* i.e. uint8_t <variable_name>; */

/******************************************************************************/
/* Main Program */
/******************************************************************************/

void main(void)
{

 /* Initialize I/O and Peripherals for application */
 InitApp(); /* richiamo la funzione di inizializzazione in user.c*/

 ledColor = 0; /* set a ledColor variable*/

 while(1)
 {
 if(tempo == 500) /* Verifico che siano passati 500ms*/
 {
 led(ledColor); /* call led function in charlieplexing.c*/
 ledColor++; /* incremento il valore */
 if (ledColor == 7) ledColor = 1; /* quando il valore del led è 7
 riparte da 0 ( il led 7 non esiste)*/
 tempo = 0; /* Azzero il contatore */
 }
 }

}

Ho analizzato i file principali che contengono il codice di programmazione che può servire per gestire il PIC12F675 e nel nostro caso per far accendere in sequenza 6 led collegati con la tecnica del charlieplexing.
Qui trovate il file zippato contenente tutto il codice sorgente.

Zipdownloadmdn75pic12F675 charliePlexing 6Led

 

Se questo articolo vi è sembrato interessante e vi è stato d’aiuto, condividetelo.
Aiutami a mantenere online il sito con un piccolo aiuto:  paypal.
Grazie infinite!