Programació en C del PIC 16F690

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

Sonòmetre digital

Programa del grup 1

En aquest cas, el programa contempla dos modes de funcionament que es poden triar amb els polsadors, el mode públic i el mode professional. En el mode públic s'indica només el nivell de so classificat en alt, mitjà o baix i es mostra el dibuix corresponent a la matriu de LED. En el mode professional es fa la mitjana de diverses mesures del valor eficaç, s'escala el valor i el resultat es mostra a la pantalla. En cas que el nivell sigui molt elevat, també sona el brunzidor.

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
#define PolsadorMenu 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
// VARIABLES
unsigned int Rms_acumulat = 0;  // sumatori dels multiples RMS abans de fer la mitja
unsigned int mitja_Rms;  // Mitjana dels multiples RMS
char contador = 0;  // Contador de quants RMS es porten calculats abans de fer la mitjana
char Vcontador;  // nre de veguades que calculem el RMS
char mitja_ON;  // indica si estem en el mode Mitja 
char Mode;  // 0-pro, 1-públic
char ValorC;  // Valor llindar general
char vPR;  // Valor mode pro
char vPB;  // Valor mode public
char arrValorPB[3] = {40,70,100};  // Valors llindar pel modes públics LOW-MED-HI
char Polsad;  // Valor polsador
char TextP[9] = {'L','O','W','M','E','D','H','I',' '};  // Text mostrat a la configuració del mode públic
char Text1[6] = {'P','R','O','P','U','B'};  // Text mostrat amb informació del mode on ens trobem
char Text2[6] = {'C','O','N','F','I','G'};  // Text de configuració
char Text3[7] = {'M','I','T','J','A','N','A'};  // Text quan s'està calculant la mitjana
// Caracters especials
char logo1[8] = {0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00010001, 0b00011001, 0b00011001};
char logo2[8] = {0b00011001, 0b00011111, 0b00001111, 0b00000110, 0b00000000, 0b00000000, 0b00000000, 0b00000000};
char logo3[8] = {0b00000000, 0b00000000, 0b00001100, 0b00011110, 0b00011110, 0b00010011, 0b00010011, 0b00010011};
char logo4[8] = {0b00010011, 0b00000001, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000};
unsigned int Lectura[16];			// Per guardar els valors llegits
unsigned int intensitatAnalog;  // valor de la intensitat sonora
unsigned int intensitatPerc;  // valor de la intensitat de 0-100
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
char fig[3][8] =  { {0b00000001, 0b00000011, 0b00000111, 0b10001110,  
  0b11011100, 0b01111000, 0b00110000, 0b00000000},
  {0b00011000, 0b00111100, 0b00100100, 0b01100110,
  0b01111110, 0b11100111, 0b11111111, 0b00000000},
  {0b11000011, 0b11100111, 0b01111110, 0b00111100,
  0b00111100, 0b01111110, 0b11100111, 0b11000011} };
char prevPos = 4;  // Guarda la figura que s'ha mostrat al bucle anterior a la matriu de LED
  // Prendra un valor entre 0 i 2 però s'hinicialitza a 4 per assegurar-nos que es mostra alguna figura
// FUNCIONS
void Pro(void);  // Mode Pro
void Config(void);  // Configuració
char Polsador(void);  // Quin dels 5 polsadors età actiu
void EnviaL(char Caracter);  // Envia un caràcter
void Esborra(void);  // Esborra la pantalla i posa el cursor a l'inici
void escriuValor(unsigned int Valor);  // Escriu un int a la pantalla LCD
void Cursor(char Filera, char Columna);  // Posa el cursor a la posició desitjada
void DefCarac(char Numero, char Fileres[8]);// Defineix caracters especials
void Avalint(void);  // avalua el nivell d'intensitat mitjana
void Canvi(void);  // Canvia l'escala i dona in valor a la intensitatPerc
void Rms(void);  // Fa el valor eficaç de les lectures
void TocaNota(char ValPR2, char ValCCPR1L, char ValDC1B);
void Envia3max(char Valor_M[]);  // Envia un joc de valors als tres MAX7221
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
void main (void) {
  // ESTABLIR PARÀMETRES
  OPTION_REG = 0b10000101;
  // Com a temporitzador basat en rellotge
  // 001 - Factor d'escala de 4
  // I resistències de pull-up desactivades
  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
  // La resta del port C és de sortida
  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
  PIR1bits.TMR2IF = 0;  // Desactiva el bit d'interrupció del Timer 2
  T2CON = 0b00000011;  // Configura el Timer 2
  ADCON0 = 0b00001001;  // Activa el conversor connectat a AN8 i AN2
  Ini3max();  // Inicialitza els tres MAX7221
  Actiu = 1;  // Activa el color vermell
  TMR0 = 100;  // Presselecció de 100, que són 156 iteracions
  // Correspon a una interrupció cada 7,5 ms
  INTCON = 0b10100000;  // Activem GIE i T0IE
  Apaga();  // Apaga tots els LED de la matriu
  // VARABLES INICIALS
  Mode  = 1;  // mode pro com a predeterminat
  vPR  = 80;  // Valor llindar inicial mode pro
  vPB  = 1;  // Valor mode Públic - MED
  Vcontador = 4;  // Màxim valor que podrà prendre el contador
  mitja_ON = 0;  // Iniciem sense calcular la mitjana
  // LOGO
  DefCarac(0, logo1);  // Defineix el caracter 0
  _delay(50);
  DefCarac(1, logo2);  // Defineix el caracter 1
  _delay(50);
  DefCarac(2, logo3);  // Defineix el caracter 2
  _delay(50);
  DefCarac(3, logo4);  // Defineix el caracter 3
  _delay(50);
  Esborra();  // Esborra la pantalla LCD
  Cursor(1, 8);  // Cursor a la primera posició
  EnviaL(0);  // Escriu el caracter 1
  Cursor(1, 9);  // Cursor a la tercera posició
  EnviaL(2);  // Escriu el caracter 3
  Cursor(2, 8);  // Cursor a la segona posició
  EnviaL(1);  // Escriu el caracter 2
  Cursor(2, 9);  // Cursor a la cuarta posició
  EnviaL(3);  // Escriu el caracter 4
  while(1){  // Bucle infinit (mostrar el logo)
    Polsad  = Polsador();  // Comprova si algun polsador està actiu
    if (Polsad != 0 || PolsadorMenu == 0)  // Si algun està actiu
      break;  // Surt del bucle
  }
  // BUCLE PRINCIPAL
  while(1){  // Bucle infinit
    for (char k = 0; k < 16; k++){  // Llegim 32 valors
      while (INTCONbits.T0IF == 0)  // Mira si Timer0 ha arribat a zero
        ;  // Si no hi ha arribat, espera
      TMR0 = 100;  // Presselecció de 100, que són 156 iteracions
      INTCONbits.T0IF = 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
      __delay_ms(1);  
    }
    __delay_ms(500);  // Delay per donar temps entre mesures
    Rms();  // fem la mitjana dels 32 valors llegits per el sensor
    Rms_acumulat = Rms_acumulat + intensitatAnalog; // acumulem el valor;
    contador++;  // Augmentem en un el valor del contador
    if (contador >= Vcontador){  // Quan el contador es igual o superior al màxim preestablert
      mitja_Rms = Rms_acumulat/contador;  // Calcula la mitjana en funció dels cicles que s'ha trigat a calcular  
      contador = 0;  // Reinicia el contador 
      Rms_acumulat = 0;  // Reinicia el valor del sumatori abans de la mitjana
      Esborra();  // Esborra la pantalla LCD
      Cursor(1,14);  // Posa el cursor alineat a la dreta
      for (char k = 0; k < 3; k++){ 
        EnviaL(Text1[k+Mode*3]);  // Escriu el text PRO o PUB segons el mode
      }
      Canvi();  // transforama la intensitat llegida a tant per cent
      Avalint();  // Encén la matriu led o comprova si s'ha superat el valor llindar
      if (Mode == 0)  // Si estem al mode professional 
        Pro();  // Executem la funció professional
      else  // Si estem al mode públic
        ValorC = arrValorPB[vPB];  // Canviem el valor llindar al que toqui
      if (PolsadorMenu == 0) {  // Si es prem el polsador 0
        while (PolsadorMenu == 0) // Mentre el polsador estigui premut
          ;  // No fa res  
        Config();  // Quan es deixa anar anem a la CONFIGURACIÓ
      }
      __delay_ms(10);  
    }
  }
}
void __interrupt() temporit(void){
  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 Pro(void){
  Polsad = Polsador();  // Comprova quin polsador està actiu
  Cursor(2, 1);  // Segona fila
  escriuValor(intensitatPerc);  // Escriu el valor de la intensitat
  ValorC = vPR;  
  if (mitja_ON == 1){  // MITJANA
    // Toca una nota
    CCP1CON = 0b00001100;  // Configura el PWM, bits P1M (bits 7-6) a 00 mode senzill
    CCPR1L = 49;  // Valor que correspon a un cicle del 35 % a 440 Hz
    TocaNota(158, 79, 2);  // Toca dos notes par alertar que ja s'ha calculat la mitjana
    TocaNota(158, 79, 2); 
    // Escriu valor
    Cursor(2, 1);  // Segona fila
    escriuValor(intensitatPerc);  // Escriu el valor de la intensitat
    __delay_ms(3000);  // Delay per mostrar el valor mitjà més estona
    mitja_ON = 0;  // torna a l'estat normal
    Vcontador = 4;  // Estableix al valor màxim del contador més petit per calcular mnys valors
  }
  if (Polsad == 3){  // Si es prem el pulsador 3
    Esborra();  // Esborra la pantalla
    for (char k = 0; k < 7; k++)// Escriu el text MITJANA
    EnviaL(Text3[k]);  
    while (Polsad == 3)  // Mentre el polsador 3 estigui premut
      Polsad = Polsador();  // Comprova si s'ha deixat de premer
    mitja_ON = 1;  // Ves a l'estat de calcular mitjana
    Vcontador = 12;  // Estableix al valor màxim del contador més gran per calcular més valors
  }
  return;  // Tornar al bucle principal
}
void Config(void){  // Menu de configuració
  while (1){  // Bucle infinit
    Polsad = Polsador();  // Lectura polsadors
    Apaga();  // Apaga la matriu de LED 
    // TÍTOL
    Esborra();  // Esborra pantalla LCD
    for (char k = 0; k < 6; k++)  // Escriu el text CONFIGURACIO
      EnviaL(Text2[k]);
    EnviaL(' ');
    EnviaL('-');
    EnviaL(' ');
    for (char k = 0; k < 3; k++)
      EnviaL(Text1[k+Mode*3]);  // Escriu el text PRO o PUB segons el mode
    if (Mode == 0){  // COSES ESPECÍFIQUES MODE PRO
      // Escriu valor
      Cursor(2, 1);  // Segona fila
      escriuValor(vPR);  // Escriu el valor llindar del mode professional
      // Lógica de polsadors
      if (Polsad == 5){  // Si es prem el polsador 5
        Mode = 1;  // Canvia al mode 1
        while (Polsad == 5)  // Mentre no es deixi anar el polsador
          Polsad = Polsador();  // Comprova si s'ha deixat anar
      }
      if (Polsad == 1 && vPR > 0){  // Si es prem el polsador 1 i no estem en el valor mínim
        vPR--;  // Baixa el valor llindar del mode professional una posició 
        __delay_ms(20);  // Delay perque no baixi de cop
      }
      if (Polsad == 2 && vPR < 100){  // Si es prem el polsador 2 i no estem en el valor màxim
        vPR++;  // Puja el valor llindar del mode professional una posició 
        __delay_ms(20);  // Delay perque no pugi de cop
      }
    }
    if (Mode == 1){  // COSES ESPECÍFIQUES MODE PÚBLIC
      // Escriu valor
      Cursor(2, 3);  // Segona fila
      for (char k = 0; k < 3; k++)  // text valor llindar mode públic
        EnviaL(TextP[k + vPB*3]);  // (LOW, MED, HI )
      // Lógica de polsadors
      if (Polsad == 5){  // Si es prem el polsador 5
        Mode = 0;  // Canvia al mode 0
        while (Polsad == 5)  // Mentre no es deixi anar el polsador
          Polsad = Polsador();  // Comprova si s'ha deixat anar
      }
      if (Polsad == 1 && vPB > 0){  // Si es prem el polsador 1 i no estem en el valor mínim
        vPB--;  // Baixa el valor llindar del mode públic una posició 
        while (Polsad == 1)  // Mentre no es deixi anar el polsador
          Polsad = Polsador();  // Comprova si s'ha deixat anar
      }
      if (Polsad == 2 && vPB < 2){  // Si es prem el polsador 2 i no estem en el valor màxim
        vPB++;  // Puja el valor llindar del mode públic una posició 
        while (Polsad == 2)  // Mentre no es deixi anar el polsador
          Polsad = Polsador();  // Comprova si s'ha deixat anar
      }
    }
    __delay_ms(20);  // Delay perque no s'actualitzi instantàniament la pantalla
    if (PolsadorMenu == 0) {  // SORTIR DEL CONFIG
      while (PolsadorMenu == 0) // Mentre el polsador estigui premut
        ;  // No fa res  
      contador = 0;  // Reinicia els valors per una bona mesura inicial
      Rms_acumulat = 0;  
      prevPos = 4;  // Posa la figura anterior com una no existent
      return;  // Quan deixis de polsar surt del bucle
    }
  }
}
void Avalint(void) {  // introduim en valor mesurat % i el comparem amb el llindar definit a config. Encenem leds  
  char pos;  // indica la figura que s'està mostrant a la matriu de LED
  char color[2] = {0,2};  // indica el color de la figura que es mostra, s'inicialitza en aquest cas groc
  if (intensitatPerc < ValorC/3){
    pos = 0;  // pos 1 correspon a la segona figura
    color[0] = 2;  // El color de la primera figura és verd
  }
  if (intensitatPerc >= ValorC/3 && intensitatPerc < ValorC*2/3){
    pos = 1;  // pos 1 correspon a la segona figura
    // Deixa el color en groc que és el de la segona figura
  }
  if (intensitatPerc >= ValorC*2/3 ){
    pos = 2;  // pos 1 correspon a la segona figura
    color[1] = 0;  // El color de la primera figura és verd
  }
  if (intensitatPerc >= ValorC){
    if (Mode ==1 && vPB != 0){
      CCP1CON = 0b00001100;  // Configura el PWM, bits P1M (bits 7-6) a 00 mode senzill
      CCPR1L = 49;  // Valor que correspon a un cicle del 35 % a 440 Hz
      TocaNota(158, 79, 2);  // Si el valor obtingut supera al llindar toca una nota per alertar
    }  
  }
  // MARTIU DE LED
  if (Mode ==1 && prevPos != pos){  // S'envia una figura a la matriu si estem en el mode 1 i la figura d'abans no és la mateixa que volem mostrar
    for (signed char k = 0; k < 8; k++){
      Sortida[1] = k+1;  // Filera
      Sortida[3] = k+1;
      Sortida[5] = k+1;
      Sortida[color[0]] = fig[pos][k];  // Color 1
      Sortida[color[1]] = fig[pos][k];  // Color 2
      Envia3max(Sortida);  // Ho envia al MAX7221
      __delay_ms(1);
    }
    prevPos = pos;  // Establir la figura anterior com l'actual al acabar de enviar-la
  }  
}
void Canvi(void){  // Dona un valor a intencitatPercent entre 0 i 100 en funció del valor de la intensitat real
  if (mitja_Rms <= 5)
    intensitatPerc=0;
  else if (mitja_Rms <= 80)
    intensitatPerc=(unsigned int)(mitja_Rms - 0) * (58 - 0) / (80 - 5) + 0;
  else if (mitja_Rms <= 400)
    intensitatPerc=(unsigned int)(mitja_Rms - 80) * (80 - 58) / (400 - 80) + 40;
  else if (mitja_Rms <= 900)
    intensitatPerc=(unsigned int)(mitja_Rms - 400) * (100 - 80) / (900 - 400) + 80;
  else
    intensitatPerc=100;
}
void Rms(void){
  unsigned int total = 0;
  unsigned int mitjana;
  unsigned long sumatori = 0;
  for (char x = 0; x < 16; x++) {
    total = total + Lectura[x];  // fem una suma total dels valors llegits
  }
  mitjana = total/16;  
  for (char x = 0; x < 16; x++) {
    sumatori = sumatori + ((Lectura[x]-mitjana)*(Lectura[x]-mitjana));  // fem una suma total dels valors llegits
  }
  intensitatAnalog = (unsigned int) (sumatori/16)/16;  
}
void TocaNota(char ValPR2, char ValCCPR1L, char ValDC1B) {
  TRISC = 0b01100000;  // Definim com volem les E/S del port C
  // 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 dona 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
  __delay_ms(200);  // Retard de 0,2 s
  TRISC = 0b01100000;  // Posem RC5 (sortida del PWM) com a entrada
  // O sigui, silenci
  __delay_ms(200);  // Retard de 0,2 s
}
char Polsador(void) {
  ADCON0 = 0b00001001;  // Activa el conversor connectat a AN8 i AN2
  __delay_ms(1);
  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
  ADCON0 = 0b10100001;  // Activa el conversor connectat a AN8 i AN2
  return Pols;
}
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;  // Centenes
  Valor = Valor / 10;
  Digits[4] = Valor % 10;  // Milers
  // Passem els dígits a ASCII
  for (char 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') {  // I mirem si ho és el quart
    Digits[4] = ' ';  // Si ho és, hi posem un espai
    if (Digits[3] == '0') {  // I mirem si ho és el tercer
      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
    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
}
void DefCarac(char Numero, char Fileres[8]) {
  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 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
  char Bytes[6];  // Els sis bytes que cal enviar
  for (unsigned char j = 1; j <= 8; j++){  // Hem d'enviar 8 fileres
    Bytes[1] = j;  // Filera
    Bytes[3] = j;
    Bytes[5] = j;
    Bytes[0] = 0x00;  // Vermells
    Bytes[2] = 0x00;  // Verds
    Bytes[4] = 0x00;  // 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.