Programació en C del PIC 16F690

Referència Trucs Perifèrics   Recursos CITCEA
Tutorial Exemples Projectes   Inici

Sonòmetre digital

Programa del grup 3

En aquest cas, el programa contempla quatre modes de funcionament. En el mode 1, que és el més bàsic, es mostra el valor eficaç i l'amplitud de l'ona de so. A més, hi ha una representació gràfica a base de barres verticals, la darrera de les quals pot estar parcialment plena per donar una imatge més visual del nivell de so. En el mode 2, a més, el brunzidor també informa del nivell de so emetent una nota que té una freqüència més aguda a mesura que augmenta el so. En el mode 3 es calcula, també, la mitjana dels valors eficaços llegits durant un cert temps i en el mode 4 es fa servir addicionalment la matriu de LED per mostrar si el nivell de so és adequat o elevat.

Pantalla

El programa és el següent:

#pragma config FOSC = INTRCIO, WDTE = OFF, PWRTE = OFF, MCLRE = OFF, CP = OFF
#pragma config CPD = OFF, BOREN = OFF, IESO = OFF, FCMEN = OFF
#include "pic16f690.h"  // Carrega el fitxer d'adreces i paràmetres del PIC 16F690
#include <xc.h>  // Carrega el fitxer de funcions
#define _XTAL_FREQ  4000000  // La freqüència del rellotge és 4 MHz
#define Polsador0 RA3  // Li assigna un nom a l'adreça del polsador de dalt
#define FiTimer1 PIR1bits.TMR1IF  // Li assigna un nom al bit que indica el final del Timer 1
unsigned int Lectura[32];  // Per guardar els valors llegits
unsigned int valors[4];  // [ve,max,min,ve_escala]
char Mode;  // Per guardar el mode en el que ens trobem
long sumatorimode3;  
char Polsad;  // Polsador que s'ha premut
char Polsad34;  // Polsador que s'ha premut en el mode 3 i en mode 4
char nombreiter;  // Temps aaproximat agafant valors en el mode3
char valormin;  // Valor minim (limit) en el mode 4  
char c1;  // Constant de configuracio del brunzidor per PWM
char c2;  // Constant de configuracio del brunzidor per PWM
char Omega[8] = {0b00011111, 0b00011111, 0b00011111, 0b00011111, 0b00011111, 0b00011111,
  0b00011111, 0b00000000};  // definim un caracter especial
char fig[6][8] = {  {0b11111111, 0b11000011, 0b10100101, 0b10011001,  // Figura 1
  0b10011001, 0b10100101, 0b11000011, 0b11111111},
  {0b00000000, 0b00000000, 0b00000000, 0b00000000, 
  0b00000000, 0b00000000, 0b00000000, 0b00000000},
  {0b00000000, 0b00000000, 0b00000000, 0b00000000, 
  0b00000000, 0b00000000, 0b00000000, 0b00000000},
  {0b11111111, 0b11111110, 0b11111100, 0b11111001,  // Figura 2
  0b01110011, 0b00100111, 0b10001111, 0b11011111},
  {0b11111111, 0b11111111, 0b11111111, 0b11111111,
  0b11111111, 0b11111111, 0b11111111, 0b11111111},
  {0b11111111, 0b11111110, 0b11111100, 0b11111001,  
  0b01110011, 0b00100111, 0b10001111, 0b11011111}};
char Port;  // Gestió del port a la funció Envia_max
char Compta;  // Comptador de bits a la funció Envia_max
char Sortida[6];  // Valors a enviar al MAX7221 (48 bits)
char Sorti[6];  // Valors a enviar al MAX7221 des de la interrupció
char Actiu;  // Variable que diu quin color està actiu
  // Actiu = 0  Apagat
  // Actiu = 1  Vermell
  // Actiu = 2  Verd
  // Actiu = 3  Blau
// Definició de les funcions que farem servir 
void escriuValor(unsigned int Valor);  // Escriu un valor a la pantalla
void EnviaL(char Caracter);  // Envia un caràcter
void Esborra(void);  // Esborra la pantalla i posa el cursor a l'inici
void Cursor(char Filera, char Columna);  // Posiciona el cursor
char Polsador(void);  // Funció de lectura dels polsadors
void escriuResultat(void);  // escriu els a la pantalla el ve_escala, max, min amb de manera ordenada
void registreCalSo(void);  // Registre el so i calcula els valors
                           // característics del so en un interval de uns 20 ms
unsigned int floorSqrt(unsigned long x);  // funció que torna la part entera de la arrel quadrada 
void menu(void);  // Funcio que escriu el menu a la pantalla
void TocaNota(char ValPR2, char ValCCPR1L, char ValDC1B);  // funcio que toca una nota 
void CompPolsador0(void);  // comprova si el Polsador0 està premut
void DefCarac(char Numero, char Fileres[8]);  // Crea un caràcter definit per l'usuari
// Funcions que per fer servir la matriu led
void Envia3max(char Valor[]);  // Envia un joc de valors als tres MAX7221
                               // desactivant interrupcions
void Envia_max(void);  // Envia un joc de valors als tres MAX7221
void Ini3max(void);  // Inicialitza els tres MAX7221
void Apaga(void);  // Apaga tots els LED de la matriu
void main (void) {
  __delay_ms(1000);  // retard de 1 segon perquè s'inicialitzi bé la pantalla
  TRISA = 0xFF;  // Tot el port A és d'entrada
  TRISB = 0;  // Tot el port B és de sortida
  TRISC = 0b01000000;  // Posa RC6 (AN8) com a entrada
  // RC5 (sortida del PWM), de moment, com a entrada
  ADCON1 = 0b00010000;  // Posa el conversor a 1/8 de la freqüència
  TXSTAbits.BRGH = 1;  // Configuració de velocitat
  BAUDCTLbits.BRG16 = 0;  // Paràmetre de velocitat de 8 bits
  SPBRG = 25;  // Velocitat de 9600 baud
  TXSTAbits.SYNC = 0;  // Comunicació asíncrona
  TXSTAbits.TX9 = 0;  // Comunicació de 8 bits
  ANSEL = 0b00000101;  // Configura AN0 i AN2 com entrada analògica
  ANSELH = 0b00000001;  // Configura AN8 com entrada analògica
  PORTB = 0;  // Inicialitza a 0 el port B
  PORTC = 0;  // Inicialitza a 0 el port C
  // Configuracio Mode2
  PIR1bits.TMR2IF = 0;  // Desactiva el bit d'interrupció del Timer 2
  T2CON = 0b00000011;  // Configura el Timer 2
  // bits T2KCPS (bits 1-0) a 11 prescalat de 16
  // bit 2 (TMR2ON) a 0, Timer aturat
  // Postscaler TOUTPS (bits 6-3) no afecten al PWM
  // configuracio mode 3
  nombreiter = 30;  // temps aproximat bucle
  // Configuració de Timer0 i matriu led del mode 4
  OPTION_REG = 0b10000101;  // Configuració de Timer0
  // Com a temporitzador basat en rellotge
  // 101 - Factor d'escala de 64
  // I resistències de pull-up desactivades (valor per defecte)
  Apaga();  // Apaga tots els LED de la matriu
  Ini3max();  // Inicialitza els tres MAX7221
  Actiu = 1;  // Activa el color vermell
  TMR0 = 100;  // Presselecció de 100, que són 156 iteracions
  valormin=50;  // Valor minim (limit) predeterminat
  Mode=0;  // Inicia amb el mode0, es a dir, el menu
  while (1){
    ADCON0 = 0b00001001;  // Activa el conversor connectat a AN8 i AN2
    Polsad = Polsador();  // Llegim els polsadors
    if (Mode==0){
      Apaga();  // Apaga tots els LED de la matriu
      PORTC= 0b00000000;  // Apaga tots els LEDs
      menu();  // Funcio que escriu el menu a la pantalla
      if (Polsad == 1){
        Cursor(1,1);  // Poscionem el cursor 
        DefCarac(2,Omega);  // Defineix el caracter especial del bloc sencer 
        Esborra();  // Esborra la pantalla;
        Mode=1;  
        PORTC= 0b00000001;  // Activa el bit 0 del port C i, per tant, encén el LED0  
      }
      if (Polsad == 2){
        Cursor(1,1);  // Poscionem el cursor 
        DefCarac(2,Omega);  // Defineix el caracter especial del bloc sencer 
        Mode=2;
        PORTC= 0b00000010;  // Activa el bit 1 del port C i, per tant, encén el LED1.
      }
      if (Polsad == 3){
        __delay_ms(1000);  // Retard de 1 segon
        Polsad34=1;  // comença el mode3 en la configuracio dels segons
        Mode=3;
        PORTC= 0b00000100;  // Activa el bit 2 del port C i, per tant, encén el LED2.  
      }
      if (Polsad == 4){
        __delay_ms(1000);  // retard de 1 segon
        Polsad34=1;
        Mode=4;
        Esborra();  // Esborra la pantalla;
        PORTC= 0b00001000;  // Activa el bit 3 del port C i, per tant, encén el LED3.
      }
    }
    if (Mode==1){
      registreCalSo();  // Registre el so i calcula els valors característics del so
      escriuResultat();  // Escriu a la pantalla el ve_escala, max,min
      CompPolsador0();  // comprova si el Polsador0 està premut  
      __delay_ms(500);  // retard de 1 segon
      CompPolsador0();  // comprova si el Polsador0 està premut
      Esborra();  // Esborra la pantalla;
    }
    if (Mode==2){
      CompPolsador0();  // comprova si el Polsador0 està premut 
      registreCalSo();  // Registre el so i calcula els valors característics del so
      escriuResultat();  // Escriu a la pantalla el ve_escala, max,min
      CCP1CON = 0b00001100;  // Configura el PWM, bits P1M (bits 7-6) a 00 mode senzill
      // DC1B = 11 (bits 5-4) els dos bits de menys pes són 0
      // CCP1M = 11xx en mode senzill els bit 0 i 1 no afecten
      // Ho posa com a configuració del PWM
      CCPR1L = 49;  // Valor que correspon a un cicle del 35 % a 440 Hz
      // Registre que ens dona l'amplada de tON
      // sonen notes depenen del so que hi ha.
      c1=238-(6/5)*valors[3];  // escala linieal corresponent a la configuracio
                               // del Timer2, bit PR2, segons el ve
      c2=119-(3/5)*valors[3];  // escala linieal corresponent a la configuracio
                               // del CCPR1L del brunzidor segons el ve
      TocaNota(c1, c2, 2);  // Valor que correspon aproximadament a la3-nota aguda
      __delay_ms(200);  // Retard de 0,2 s
      TocaNota(c1, c2, 2);  // Valor que correspon aproximadament a la3-nota aguda
      CompPolsador0();  // comprova si el Polsador0 està premut
      __delay_ms(1000);  // retard de 1 segon
      Esborra();  // Esborra la pantalla;
    }
    if (Mode==3){ // aquest mode esta uns segons aproximats(nombreiter), on cada segon calcula el ve
                  // i llavors torna el valor mitja dels ve calculats
      __delay_ms(200);  // retard de 0.2 segon 
      sumatorimode3=0;
      CompPolsador0();  // comprova si el Polsador0 està premut
      if (Polsad34==1){
        Esborra();  // Esborra la pantalla;
        EnviaL('S');  // Lletra
        EnviaL('E');  // Lletra
        EnviaL('G');  // Lletra
        EnviaL('O');  // Lletra
        EnviaL('N');  // Lletra
        EnviaL('S');  // Lletra
        Cursor(2,1);  // Poscionem el cursor 
        escriuValor(nombreiter);  // Escrivim el valor minim
      }
      Polsad34 = Polsador();  // Llegim els polsadors
      if (Polsad34==3){
        if (nombreiter<60){
          nombreiter++;  // Increment nombre iteracions (segons aproimats)
        }
        Polsad34=1;  // fem que entri en el altre if
      }
      if (Polsad34==4){
        if (nombreiter>1){
          nombreiter--;  // Decrement nombre iteracions (segons aproimats)
        }
        Polsad34=1;  // fem que entri en el altre if
      }
      if (Polsad34==2){
        Esborra();  // Esborra la pantalla;
        EnviaL('E');  // Lletra
        EnviaL('S');  // Lletra
        EnviaL('P');  // Lletra
        EnviaL('E');  // Lletra
        EnviaL('R');  // Lletra
        EnviaL('A');  // Lletra
        EnviaL('N');  // Lletra
        EnviaL('T');  // Lletra
        for (char k = nombreiter; k !=0; k--){  // Llegim tants valors com el nombreiter
          registreCalSo();  // Registre el so i calcula els valors característics del so
          sumatorimode3=sumatorimode3+valors[3];  // suma el valor eficaç en el sumatori
          __delay_ms(800);  // retard de 0.8 segons determinat experimentalment perque es calculi ve cada segon 
        } 
        unsigned int veMitja= sumatorimode3/nombreiter;
        Esborra();  // Esborra la pantalla;
        EnviaL('V');  // Lletra
        EnviaL('E');  // Lletra
        EnviaL(' ');  // Lletra
        EnviaL('M');  // Lletra
        EnviaL('I');  // Lletra
        EnviaL('T');  // Lletra
        EnviaL('J');  // Lletra
        EnviaL('A');  // Lletra
        Cursor(2,1);  // Poscionem el cursor 
        escriuValor(veMitja);  // Escrivim valor mitja a la pantalla
        CompPolsador0();  // comprova si el Polsador0 està premut
        __delay_ms(1000);  // retard de 1 segon
      }
    }
    if (Mode==4){
      if (Polsad34==1){
        Esborra();  // Esborra la pantalla;
        Cursor(1,1);  // Poscionem el cursor 
        EnviaL('L');  // Lletra
        EnviaL('I');  // Lletra
        EnviaL('I');  // Lletra
        EnviaL('M');  // Lletra
        EnviaL('I');  // Lletra
        EnviaL('T');  // Lletra
        EnviaL(' ');  // Lletra
        EnviaL('V');  // Lletra
        EnviaL('E');  // Lletra
        Cursor(2,5);  // Poscionem el cursor 
        escriuValor(valormin);  // Escrivim el valor minim
      }
      Polsad34 = Polsador();  // Llegim els polsadors
      if (Polsad34==3){
        if (valormin<100){
          valormin+=5;  // Increment del valor minim (limit)
        }
        Polsad34=1;  // fem que entri en el altre if
      }
      if (Polsad34==4){
        if (valormin>1){
          valormin-=5;  // Decrement del valor minim (limit)
        }
        Polsad34=1;  // fem que entri en el altre if
      }
      CompPolsador0();  // comprova si el Polsador0 està premut
      registreCalSo();  // Registre el so i calcula els valors característics del so
      INTCON = 0b10100000;  // Activem GIE i T0IE
      for (char k = 0; k < 8; k++){
        Sortida[1] = k+1;  // Filera
        Sortida[3] = k+1;
        Sortida[5] = k+1;
        if (valors[3]>=valormin){ 
          Sortida[0] = fig[0][k];  // Vermells
          Sortida[2] = fig[1][k];  // Verds
          Sortida[4] = fig[2][k];  // Blaus
          Envia3max(Sortida);  // Ho envia al MAX7221
          __delay_ms(1);  // Donem temps
        }
        if (valors[3]<valormin){ 
          Sortida[0] = fig[3][k];  // Vermells
          Sortida[2] = fig[4][k];  // Verds
          Sortida[4] = fig[5][k];  // Blaus
          Envia3max(Sortida);  // Ho envia al MAX7221
          __delay_ms(1);  // Donem temps
        }
      }
      CompPolsador0();  // comprova si el Polsador0 està premut
    }
  }
}
void __interrupt() temporit(void){ // INTERRUPCIONS PEL TIMER0
  if (INTCONbits.T0IF) {  // Comprovem que hi ha interrupció per Timer 0
    TMR0 = 100;  // Preselecció de Timer0
    INTCONbits.T0IF = 0;  // Desactiva el bit que indica interrupció pel Timer0
    if (Actiu != 0) {  // Si la matriu no està apagada
      Actiu--;  // Passem a activar un altre color
      if (Actiu == 0) {  // Si hem arribat a zero
        Actiu = 3;  // Torna a posar el 3
      }
    }
    // D'entrada els desactivem els tres
    Sorti[0] = 0x00;  // Vermell
    Sorti[2] = 0x00;  // Verd
    Sorti[4] = 0x00;  // Blau
    if (Actiu == 1) {  // Si és vermell
      Sorti[0] = 0x01;  // Vermell activat
    }
    if (Actiu == 2) {  // Si és verd
      Sorti[2] = 0x01;  // Verd activat
    }
    if (Actiu == 3) {  // Si és blau
      Sorti[4] = 0x01;  // Blau activat
    }
    Sorti[1] = 0x0C;  // Shutdown mode
    Sorti[3] = 0x0C;  // Shutdown mode
    Sorti[5] = 0x0C;  // Shutdown mode
    Envia_max();  // Ho envia al MAX7221
  }
}
void CompPolsador0(void){ // comprovacio del polsador0
  for (char k=11;k!=0; k--){ // farem la comprovació del polsador 10 vegades
    if (Mode!=0){
      if (Polsador0 == 0){
        Mode=0;  // canvi de mode al mode 0
        Apaga();  // Apaga tots els LED de la matriu
        __delay_ms(100); // retard de 0.1 segon
      }
    } else {
      return;  // sino es premut el polsador no passa res
    }
  }
}
// https:// www.geeksforgeeks.org/square-root-of-an-integer/
unsigned int floorSqrt(unsigned long x){ // funció que torna la part entera de la arrel quadrada 
  // Base cases
  if (x == 0 || x == 1){
    return x;
  }
  // Do Binary Search for floor(sqrt(x))
  int start = 1,ans;
  unsigned long end = x; 
  while (start <= end){  
    unsigned long mid = (start + end) / 2;
    // If x is a perfect square
    if (mid*mid == x){
      return mid;
    }
    // Since we need floor, we update answer when mid*mid is
    // smaller than x, and move closer to sqrt(x)
    if (mid*mid < x){
      start = mid + 1;
      ans = mid;
    } else { // If mid*mid is greater than x
      end = mid-1;
    }
  }
  return ans;
}
void registreCalSo(void){  // Registre el so i calcula els valors característics del so en un interval de uns 20 ms
  FiTimer1 = 0;  // Aquest bit es posarà a 1 quan el temporitzador acabi
  // cal desactivar-lo des del programa
  T1CON = 0b00100001;  // Configuració de Timer1
  // 10 - Factor d'escala de 4
  // 1 - comença a comptar
  TMR1H=255;  // preselecció perque llegeixi un valor del so en 0.620 ms
  TMR1L=100;  // i per tant quan llegeixi 32 valors serà en un interval total de uns 20 ms
  ADCON0 = 0b10100001;  // Activa el conversor connectat a AN8 i AN2
  valors[1] = 0;  // Definim el maxim
  valors[2] = 10000;  // Definim el minim
  unsigned long sum = 0;  // Definim una variable per emmagatzemar la suma de tots els valors del so
  for (int k = 0; k < 32; k++){  // Llegim 32 valors
    while (FiTimer1 == 0)  // Mira si Timer0 ha arribat a zero
      ;  // Si no hi ha arribat, espera
    TMR1H=255;
    TMR1L=100;  
    FiTimer1 = 0;  // Desactivem el bit de final de temporització
    ADCON0bits.GO = 1;  // Posa en marxa el conversor
    while (ADCON0bits.GO == 1)  // Mentre no acabi
      ;  // ens esperem
    Lectura[k] = ADRESH * 256 + ADRESL;  // Guardem el resultat
    sum += Lectura[k];  // Els anem sumant
    if (Lectura[k] > valors[1]){  // Comprovem si es el maxim  
      valors[1] = Lectura[k];  // Si ho es, guardem el valor
    }
    if (Lectura[k]< valors[2]){  // Comprovem si es el minim
      valors[2] = Lectura[k];  // Si ho es, guardem el valor
    }
  }
  unsigned long mean = sum/32;  // Calculem la mitjana dels valors  obtinguts del so
  sum = 0;  // Posem el sumatori a 0 per reutilitzar la variable
  for (int k = 0; k < 32; k++){  // Llegim 32 valors
    sum += (Lectura[k]-mean)*(Lectura[k]-mean); // Elevem al quadrat
  }
  // valors[0] = (unsigned int)sqrt(sum/32);  // v.e.
  valors[0] = (unsigned int)floorSqrt(sum/32);  // calcul del valor eficaç i guardar-lo
  // valor eficaç en una escala determinada
  valors[3]=(valors[0]*100)/511;  // ve_escala=(valors[1]*100)/511
  return;
}
void menu(void){
  Esborra();  // Esborra la pantalla;
  EnviaL('M');  // Lletra
  EnviaL('O');  // Lletra
  EnviaL('D');  // Lletra
  EnviaL('1');  // Lletra
  EnviaL(' ');  // Lletra
  EnviaL('M');  // Lletra
  EnviaL('O');  // Lletra
  EnviaL('D');  // Lletra
  EnviaL('2');  // Lletra
  EnviaL(' ');  // Lletra
  EnviaL('M');  // Lletra
  EnviaL('O');  // Lletra
  EnviaL('D');  // Lletra
  EnviaL('3');  // Lletra
  Cursor(2,1);  // Poscionem el cursor 
  EnviaL('M');  // Lletra
  EnviaL('O');  // Lletra
  EnviaL('D');  // Lletra
  EnviaL('4');  // Lletra
  __delay_ms(100);  // retard de 0.1 segon
}
void escriuResultat(void){  // Escriu a la pantalla el ve_escala, max,min
  Esborra();  // Esborra la pantalla;
  Cursor(1,1);  // Poscionem el cursor 
  EnviaL('V');  // Lletra
  EnviaL('E');  // Lletra
  Cursor(1,4);  // Poscionem el cursor 
  escriuValor(valors[3]);  // Escriure el valor eficaç a la pantalla
  Cursor(1,7);  // Poscionem el cursor 
  EnviaL('R');  // Lletra
  EnviaL('A');  // Lletra
  EnviaL('N');  // Lletra
  EnviaL('G');  // Lletra
  Cursor(1,12);  // Poscionem el cursor 
  escriuValor(valors[1]-valors[2]);  // Escrivim el rang del so
  char bc;  // Defim variable per emmagatzemar el nombre de blocs complets
  char fb;  // Defim variable per emmagatzemar files del ultim bloc
  char Omega2[8] = {0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000,
    0b00000000, 0b00000000, 0b00000000}; // definim el caracter especial en blanc
  bc = valors[3]%7;  
  fb = valors[3]/7;
  for (char i=1;i<fb+1;i++){
    Cursor(2, i);  // Poscionem el cursor 
    EnviaL(2);  // Envia el caracter especial del bloc sencer a la pantalla
  }
  for (char n=8-bc; n<8; n++){
    Omega2[n-1] = 0b00011111;  // omplim les files
  }
  Cursor(1,1);  // Poscionem el cursor 
  DefCarac(0,Omega2);
  Cursor(2,fb+1);  // Poscionem el cursor 
  EnviaL(0);  // Envia el caracter especial del bloc parcial a la pantalla
}
void DefCarac(char Numero, char Fileres[8]) { // Crea un caràcter definit per l'usuari
  char Posicio = 0;  // Variable per a calcular la posició
  if (Numero  < 8) {  // Comprovem que sigui un valor raonable
    Posicio = Numero * 8;  // Calculem l'adreça de memòria
  }
  Posicio = Posicio + 64;  // Posa el bit de caràcter especial a 1
  EnviaL(254);  // Control de la posició del cursor
  EnviaL(Posicio);  // Canvia el cursor de lloc
  for (signed char k = 0; k < 8; k++){
    EnviaL(Fileres[k]);  // Canvia el cursor de lloc
  }
}
void escriuValor(unsigned int Valor){
  char Digits[5];  // Variable amb el número dígit a dígit
  // Digits[0] són les unitats
  // Convertim a BCD
  Digits[0] = Valor % 10;  // Unitats
  Valor = Valor / 10;
  Digits[1] = Valor % 10;  // Desenes
  Valor = Valor / 10;
  Digits[2] = Valor % 10;  // Centenes
  Valor = Valor / 10;
  Digits[3] = Valor % 10;  // Milers
  Digits[4] = Valor / 10;  // Desenes de milers
  // Passem els dígits a ASCII
  for (int j = 0; j < 5; j++){  // 5 dígits
    Digits[j] = Digits[j] + '0';  // Li sumem el codi ASCII de 0
  }
  // Suprimim zeros innecessaris
  if (Digits[4] == '0') {  // Mirem si el primer dígit és 0
    Digits[4] = ' ';  // Si ho és, hi posem un espai
    if (Digits[3] == '0') {  // I mirem si ho és el segon
      Digits[3] = ' ';  // Si ho és, hi posem un espai
      if (Digits[2] == '0') {  // I mirem si ho és el tercer
        Digits[2] = ' ';  // Si ho és, hi posem un espai
        if (Digits[1] == '0') {  // I mirem si ho és el quart
          Digits[1] = ' ';  // Si ho és, hi posem un espai
        }  // El 0 de les unitats el mostrarem sempre
      }
    }
  }
  for (int k = 4; k >= 0; k--){  // Només escrivim quatre valors
    if (Digits[k] != ' '){
      EnviaL(Digits[k]);  // Escriu un dígit a la pantalla
    }
  }
}
void EnviaL(char Caracter) {
  INTCONbits.GIE = 0;  // Desactiva les interrupcions momentàniament
  RCSTAbits.SPEN = 1;  // Activa comunicació sèrie
  TXSTAbits.TXEN = 1;  // Activa comunicació
  TXREG = Caracter;  // Agafa el caràcter i l'envia
  __delay_ms(1);  // Donem temps
  while (PIR1bits.TXIF == 0)  // Esperem que s'acabi d'enviar
    ;  // No fem res
  RCSTAbits.SPEN = 0;  // Desactiva comunicació sèrie
  TXSTAbits.TXEN = 0;  // Desactiva comunicació
  INTCONbits.GIE = 1;  // Activa les interrupcions
}
void Esborra(void) {
  EnviaL(254);  // Caràcter de control
  EnviaL(1);  // Esborra la pantalla i posa el cursor a l'inici
}
void Cursor(char Filera, char Columna) {
  char Posicio = 0;  // Variable per a calcular la posició
  if (Filera == 2) {
    Posicio = 64;  // La primera columna de la segona fila és 64;
  }
  if (Columna > 0 && Columna < 33) {  // Comprovem que sigui un valor raonable
    Posicio = Posicio + Columna;  // Sumem les adreces
    Posicio = Posicio - 1;  // Restem 1 perquè numera des de 0
  }
  Posicio = Posicio + 128;  // Posa el bit de posicionat a 1
  EnviaL(254);  // Control de la posició del cursor
  EnviaL(Posicio);  // Canvia el cursor de lloc
}
char Polsador(void) {
  char Pols = 0;
  ADCON0bits.GO = 1;  // Posa en marxa el conversor
  while (ADCON0bits.GO == 1)  // Mentre no acabi
    ;  // ens esperem
  if (ADRESH < 220 && ADRESH > 200) {
    Pols = 1;  // Comprova polsador 1
  }
  if (ADRESH < 194 && ADRESH > 174) {
    Pols = 2;  // Comprova polsador 2
  }
  if (ADRESH < 163 && ADRESH > 143) {
    Pols = 3;  // Comprova polsador 3
  }
  if (ADRESH < 90 && ADRESH > 70) {
    Pols = 4;  // Comprova polsador 4
  }
  if (ADRESH < 55 && ADRESH > 35) {
    Pols = 5;  // Comprova polsador 5
  }
  return Pols;
}
void TocaNota(char ValPR2, char ValCCPR1L, char ValDC1B) {
  TRISC = 0b01100000;  // Definim com volem les E/S del port C i continua el sonometre com a entrad
  // RC5 (sortida del PWM), de moment, com a entrada
  PR2 = ValPR2;  // Carrega PR2
  CCP1CON = CCP1CON & 0b11001111;  // Posa a zero els bits que corresponen a DC1B
  ValDC1B = ValDC1B % 4;  // DC1B va de 0 a 3
  ValDC1B = ValDC1B * 16;  // Despla?a els bits a la posici? que els correspon a CCP1CON
  CCP1CON = CCP1CON | ValDC1B;  // Coloca DC1B al seu lloc
  CCPR1L = ValCCPR1L;  // Carrega CCPR1L, registre que ens d?na l'amplada de tON
  PIR1bits.TMR2IF = 0;  // Desactiva el bit d'interrupci? del Timer 2
  T2CON = 0b00000111;  // Configura el Timer 2
  // bits T2KCPS (bits 1-0) a 11 prescalat de 16
  // bit 2 (TMR2ON) a 1, Timer activat
  // Postscaler TOUTPS (bits 6-3) no afecten al PWM
  while (PIR1bits.TMR2IF == 0)  // Espera l'activaci? del bit d'interrupci? del Timer 2
    ;  // Esperem
  TRISC = 0b01000000;  // Posem RC5 (sortida del PWM) com a sortida i continua el sonometre com a entrad
  __delay_ms(200);  // Retard de 0,2 s
  TRISC = 0b01100000;  // Posem RC5 (sortida del PWM) com a entrada i continua el sonometre com a entrada
  // O sigui, silenci
  __delay_ms(200);  // Retard de 0,2 s
}
void Envia3max(char Valor[]) {  // Envia un joc de valors als tres MAX7221
  INTCONbits.T0IE = 0;  // Desactiva les interrupcions momentàniament
  char Port = 0;  // Variable on guardem l'estat del port B
  char Temp;  // Variable temporal
  for (signed char j = 5; j >= 0; j--){  // Hem d'enviar 6 bytes
    for (signed char k = 1; k < 9; k++){  // De 8 bits
      Temp = Valor[j] & 0b10000000;  // Agafa el bit de més a l'esquerra
      // Temp només podrà valer 0 o 128
      if (Temp == 0) {  // Si val 0
        Port = Port & 0b11101111;  // Desactiva Data (bit 4)
      } else {  // Si val 128
        Port = Port | 0b00010000;  // Activa Data (bit 4)
      }
      Valor[j] = Valor[j] << 1;  // Rodem els bits per situar el següent
      PORTB = Port;  // Ho posa al port B
      Port = Port | 0b00100000;  // Activa Clock (bit 5) i força lectura
      PORTB = Port;  // Ho posa al port B
      Port = Port & 0b11011111;  // Desactiva Clock (bit 5)
      PORTB = Port;  // Ho posa al port B
    }
  }
  Port = Port | 0b01000000;  // Activa Latch (bit 6) per copiar a les sortides
  PORTB = Port;  // Ho posa al port B
  INTCONbits.T0IE = 1;  // Reactiva les interrupcions a l'acabar
}
void Envia_max(void) {  // Envia un joc de valors als tres MAX7221
  asm("banksel _Port");
  asm("bcf (_Port&7fh),5");  // S'assegura que Clock està desactivat
  asm("bcf (_Port&7fh),6");  // S'assegura que Latch està desactivat
  asm("movf (_Port&7fh),w");  // Agafa el valor de Port
  asm("movwf PORTB");  // I el posa al port B
  asm("banksel _Compta");
  asm("movlw 48");  // Número de bits a enviar
  asm("movwf (_Compta&7fh)");  // Variable per comptar els bits
  asm("Bucle:");
  asm("banksel _Port");
  asm("bcf (_Port&7fh),4");  // Desactiva Data. Si toca activar-ho, ja ho farem
  asm("banksel _Sorti");
  asm("rlf (_Sorti&7fh),f");  // Fa sortir el bit de més a l'esquerra cap a C
  asm("rlf ((_Sorti+1)&7fh),f");  // i roda els altres a l'esquerra
  asm("rlf ((_Sorti+2)&7fh),f");
  asm("rlf ((_Sorti+3)&7fh),f");
  asm("rlf ((_Sorti+4)&7fh),f");
  asm("rlf ((_Sorti+5)&7fh),f");
  asm("banksel _Port");
  asm("btfsc STATUS,0");  // Mira si el bit de l'esquerra era un 1
  asm("bsf (_Port&7fh),4");  // Si era 1, activa Data
  asm("movf (_Port&7fh),w");  // Agafa el valor de Port. El valor que ha canviat és Data
  asm("movwf PORTB");  // I el posa al port B
  asm("bsf (_Port&7fh),5");  // Activa Clock, forçant a llegir el bit
  asm("movf (_Port&7fh),w");  // Agafa el valor de Port. El valor que ha canviat és Clock
  asm("movwf PORTB");  // I el posa al port B
  asm("bcf (_Port&7fh),5");  // Desactiva Clock
  asm("movf (_Port&7fh),w");  // Agafa el valor de Port. El valor que ha canviat és Clock
  asm("movwf PORTB");  // I el posa al port B
  asm("banksel _Compta");
  asm("decfsz (_Compta&7fh),f");  // Decrementa Compta
  asm("goto Bucle");  // Si Compta no és zero, repeteix el bucle
  asm("banksel (_Port&7fh)");
  asm("bsf (_Port&7fh),6");  // Torna a activar Latch
  // Els valors es copiaran a la sortida del registre
  asm("movf (_Port&7fh),w");  // Agafa el valor de Port. El valor que ha canviat és Latch
  asm("movwf PORTB");  // I el posa al port B
}
void Ini3max(void) {  // Inicialitza els tres MAX7221
  char Bytes[6];  // Els sis bytes que cal enviar
  Bytes[0] = 0x00;  // Desactivat
  Bytes[1] = 0x0C;  // Shutdown mode
  Bytes[2] = 0x00;
  Bytes[3] = 0x0C;
  Bytes[4] = 0x00;
  Bytes[5] = 0x0C;
  Envia3max(Bytes);  // Els envia
  Bytes[0] = 0x00;  // No decode
  Bytes[1] = 0x09;  // Decode mode
  Bytes[2] = 0x00;
  Bytes[3] = 0x09;
  Bytes[4] = 0x00;
  Bytes[5] = 0x09;
  Envia3max(Bytes);  // Els envia
  Bytes[0] = 0x07;  // Vuit fileres
  Bytes[1] = 0x0B;  // Scan limit
  Bytes[2] = 0x07;
  Bytes[3] = 0x0B;
  Bytes[4] = 0x07;
  Bytes[5] = 0x0B;
  Envia3max(Bytes);  // Els envia
}
void Apaga(void) {  // Apaga tots els LED de la matriu
  char Bytes[6];  // Els sis bytes que cal enviar
  for (signed char j = 1; j <= 8; j++){  // Hem d'enviar 8 fileres
    Bytes[1] = Bytes[3] = Bytes[5] = j;  // Filera
    Bytes[0] = Bytes[2] = Bytes[4] = 0x00;  // Vermells, verds i blaus
    Envia3max(Bytes);  // Els envia
  }
}

 

 

Llicència de Creative Commons
Aquesta obra d'Oriol Boix està llicenciada sota una llicència no importada Reconeixement-NoComercial-SenseObraDerivada 3.0.