En aquest cas, el programa mostra a la pantalla la mitjana de 32 lectures del valor eficaç. També hi ha una escala gràfica, de 0 a 80, que mostra el nivell de so aprofitant que la pantalla té setze caràcters formats per cinc punts d'ample. Addicionalment, a la matriu de LED es mostra el nivell de so en una escala de vuit nivells.


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 #include <math.h> // Per calcular arrels quadrades #define _XTAL_FREQ 4000000 // La freq??ncia del rellotge ?s 4 MHz #define btn0 RA3
unsigned int Read; // Valor que llegirà el sensor unsigned int sumQuad; // Suma de quadrats dels valors per l'RMS unsigned int RMS; // Valor de l'RMS unsigned long X; // Mitjana d'RMS 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ó static char Actiu; // Variable que diu quin color està actiu unsigned int Lvl; // Valors dels nivells gràfics de la LCD i la matriu LED char Adder; // Bit que rotarem per encendre els LED que ens interessen unsigned int min; // Mínim de l'escalat unsigned int max; // Màxim de l'escalat unsigned long iter; // Iteracions per a calcular la mitjana
// Definici? de les funcions que farem servir
void write_val(unsigned int Valor); // Escriu un valor a la pantalla
void write_slong(unsigned long Valor); // Escriu un valor a la pantalla
void write_bar(unsigned int Lvl); // Dibuixa una barra de certa longitud
// de caràcters a la pantalla
void send_char(char Caracter); // Envia un car?cter
void clear_lcd(void); // Esborra la pantalla i posa el cursor a l'inici
void cursor(char Filera, char Columna); // Posiciona el cursor
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 clear_led(void); // Apaga tots els LED
void disp_scale(int increment); // Mostra min i max a la pantalla
void disp_iter(int increment); // Mostra iter i mult a la pantalla
void settings_scale(void); // Ajustaments de min i max
void settings_iter(void); // Ajustaments d'iter
void def_char(char num, char val); // Defineix els caràcters per a les barres parcials
void main (void) {
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)
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 = 0b00000001; // Configura AN0 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
min = 0;
max = 100;
iter = 32;
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
clear_led(); // Apaga tots els LED
INTCONbits.GIE = 0; // Desactiva les interrupcions momentàniament
RCSTAbits.SPEN = 1; // Activa comunicació sèrie
TXSTAbits.TXEN = 1; // Activa comunicació
__delay_ms(1500);
def_char(1, 0b00010000);
def_char(2, 0b00011000);
def_char(3, 0b00011100); // Deifinim els caràcters per a les barres parcials
def_char(4, 0b00011110);
def_char(5, 0b00011111);
clear_lcd(); // Esborra tota la pantalla
RCSTAbits.SPEN = 0; // Desactiva comunicació sèrie
TXSTAbits.TXEN = 0; // Desactiva comunicació
INTCONbits.GIE = 1; // Activa les interrupcions
ADCON0 = 0b10100001; // Activa el conversor connectat a AN8
// amb el resultat justificat per la dreta
__delay_ms(400); // Retard de 0,4 s
while (1) {
X = 0; // Ho farem servir per calcular la mitjana
// Calcularem 32 RMS i els anirem mostrant
// a la primera filera de la matriu de LED
for (int j = 0; j < iter; j++) {
sumQuad = 0; // Ho farem servir per calcular l'RMS
for (int i = 0; i < 32; i++) { // Calcularem 32 RMS
ADCON0bits.GO = 1; // Posa en marxa el conversor
while (ADCON0bits.GO == 1) // Mentre no acabi
; // ens esperem
Read = ADRESH * 256 + ADRESL; // Guardem el resultat
if (Read > 511) {
sumQuad += (Read - 511)*(Read - 511); // Restem 511 que és el valor nul
} else {
sumQuad += (511 - Read)*(511 - Read); // Restem 511 que és el valor nul
}
// Anem sumant els seus quadrats per l'RMS
// Al fer el quadrat quedarà positiu
if (btn0 == 0) {
clear_led();
INTCONbits.GIE = 0; // Desactiva les interrupcions momentàniament
RCSTAbits.SPEN = 1; // Activa comunicació sèrie
TXSTAbits.TXEN = 1; // Activa comunicació
clear_lcd();
__delay_ms(400);
settings_scale();
clear_lcd();
RCSTAbits.SPEN = 0; // Desactiva comunicació sèrie
TXSTAbits.TXEN = 0; // Desactiva comunicació
INTCONbits.GIE = 1; // Activa les interrupcions
}
}
RMS = sqrt(sumQuad/32); // Calculem l'RMS a partir de la suma de quadrats
if (RMS < min) { // Per evitar que min sigui negatiu
RMS = 0;
} else {
RMS = (RMS-min)*100/(max-min); // Apliquem l'escalat
}
X += RMS; // Ho sumem a la variable que farem servir per
// calcular la 'sub-mitjana'
Sortida[0] = 0b00000000; // Posem tots els vermells a 0
Sortida[2] = 0b00000000; // Posem tots els verds a 0
Adder = 0b10000000; // Anirem rotant aquesta variable per encendre
// els LED segons el nivell
Lvl = RMS*8/100; // Ajustem el nivell als 8 LED de la matriu
for (int i = 0; i < Lvl; i++) { // Per cada LED mirem si l'encenem o no segons el color
if (i < 7) { // Si el nivell és inferior a 7, s'encén de color verd
Sortida[2] = Sortida[2] + Adder;
}
if (i > 3) { // Si el nivell és superior a 3, s'encén de color vermell
Sortida[0] = Sortida[0] + Adder;
} // Del 4 al 6 quedaran de color groc per combinació
Adder = Adder >> 1; // Rotem per treballar amb el següent LED
}
Sortida[1] = 0x01; // Filera 1
Sortida[3] = 0x01;
Sortida[5] = 0x01;
Envia3max(Sortida); // Ho envia al MAX7221
__delay_ms(50);
}
X /= iter; // Calcula la mitjana
INTCONbits.GIE = 0; // Desactiva les interrupcions momentàniament
RCSTAbits.SPEN = 1; // Activa comunicació sèrie
TXSTAbits.TXEN = 1; // Activa comunicació
clear_lcd(); // Esborrem tot el que hi ha a la pantalla
cursor(1,6); // Movem el cursor per centrar el valor
write_val(X); // Escribim el valor
cursor(2,1); // Movem el cursor a la segona filera
write_bar(X); // Dibuixem la barra amb el nivell
RCSTAbits.SPEN = 0; // Desactiva comunicació sèrie
TXSTAbits.TXEN = 0; // Desactiva comunicació
INTCONbits.GIE = 1; // Activa les interrupcions
}
}
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 settings_scale(void) {
unsigned int increment = 1; // L'increment per canviar alguna variable (serà 1 o 10)
clear_lcd(); // Esborra la pantalla
cursor(1,5); // Mou el cursor
send_char('S');
send_char('E');
send_char('T'); // Escriu "SETTINGS"
send_char('T');
send_char('I');
send_char('N');
send_char('G');
send_char('S');
__delay_ms(400); // Retard de 400ms
disp_scale(increment); // Mostra min i max a la pantalla
while (1){
ADCON0 = 0b00001001; // Activa el conversor A/D connectat a AN2
ADCON0bits.GO = 1; // Posa en marxa el conversor
while (ADCON0bits.GO == 1) // Mentre no acabi
; // ens esperem
if (ADRESH < 220 && ADRESH > 200) { // Polsador 1
if (max > min + increment) { // max > min
max -= increment; // Redueix max
}
}
if (ADRESH < 194 && ADRESH > 174) { // Polsador 2
if (max <= 511-increment) { // max <= 511
max += increment; // Augmenta max
}
}
if (ADRESH < 163 && ADRESH > 143) { // Polsador 3
if (increment == 1) { //
increment = 10; //
} else { // Alterna l'increment entre 1 i 10
increment = 1; //
}
}
if (ADRESH < 90 && ADRESH > 70) { // Polsador 4
if (min >= increment) { // min >= 0
min -= increment; // Redueix min
}
}
if (ADRESH < 55 && ADRESH > 35) { // Polsador 5
if (min < max-increment) { // min < max
min += increment; // Augmenta min
}
}
if (ADRESH > 35) { // Si s'ha premut algun polsador
disp_scale(increment); // Mostra min i max a la pantalla
__delay_ms(100); // Retard
}
if (btn0 == 0) { // Si es prem el polsador 0
settings_iter();
ADCON0 = 0b10100001; // Activa el conversor connectat a AN8
break; // Acaba settings
}
}
}
void disp_scale(int increment) {
clear_lcd(); // Esborra la pantalla
cursor(1,0); // Mou el cursor
send_char('M');
send_char('A'); // Escriu "MAX"
send_char('X');
cursor(2,0); // Mou el cursor
send_char('M');
send_char('I'); // Escriu "MIN"
send_char('N');
cursor(1,7); // Mou el cursor
write_val(max); // Escriu el valor de la variable min
cursor(2,7); // Mou el cursor
write_val(min); // Escriu el valor de la variable max
cursor(1,13); // Mou el cursor
write_val(increment); // Escriu quant val l'increment
}
void settings_iter(void) {
unsigned int increment = 1; // L'increment per canviar alguna variable (serà 1 o 10)
clear_lcd();
__delay_ms(400); // Retard de 400ms
disp_iter(increment); // Mostra iter i mult a la pantalla
while (1){
ADCON0 = 0b00001001; // Activa el conversor A/D connectat a AN2
ADCON0bits.GO = 1; // Posa en marxa el conversor
while (ADCON0bits.GO == 1) // Mentre no acabi
; // ens esperem
if (ADRESH < 220 && ADRESH > 200) { // Polsador 1
if (iter > increment) { // iter > 0
iter -= increment; // Redueix iter
}
} else if (ADRESH < 194 && ADRESH > 174) { // Polsador 2
if (iter < 8388608-increment) { // iter < 8388608
iter += increment; // Augmenta iter
}
} else if (ADRESH < 163 && ADRESH > 143) { // Polsador 3
if (increment == 10000) { //
increment = 1; //
} else { // Alterna iter entre
increment *= 10; // 1, 10, 100, 1000 i 10000
}
}
if (ADRESH > 143) { // Si s'ha premut algun polsador
disp_iter(increment); // Mostra iter i mult a la pantalla
__delay_ms(100); // Retard
}
if (btn0 == 0) { // Si es prem el polsador 0
clear_lcd(); // Esborra la pantalla
__delay_ms(400); // Retard de 400ms
break; // Acaba settings
}
}
}
void disp_iter(int increment) {
clear_lcd(); // Esborra la pantalla
cursor(1,0); // Mou el cursor
send_char('I');
send_char('T'); // Escriu "ITER"
send_char('E');
send_char('R');
cursor(2,0); // Mou el cursor
write_slong(iter); // Escriu el valor de la variable iter
cursor(1,10); // Mou el cursor
write_slong(increment); // Escriu quant val l'increment
}
void write_val(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
}
}
}
for (int k = 3; k >= 0; k--){ // Nom?s escrivim quatre valors
send_char(Digits[k]); // Escriu un d?git a la pantalla
}
}
void write_slong(unsigned long Valor){
char Digits[7]; // 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
Valor = Valor / 10;
Digits[4] = Valor % 10; // Desenes de milers
Valor = Valor / 10;
Digits[5] = Valor % 10; // Centenes de milers
Digits[6] = Valor / 10; // Mil·lions
// Passem els d?gits a ASCII
for (int j = 0; j < 7; j++){ // 7 d?gits
Digits[j] = Digits[j] + '0'; // Li sumem el codi ASCII de 0
}
// Suprimim zeros innecessaris // Si ho ?s, hi posem un espai
if (Digits[6] == '0') { // Mirem si el primer d?git ?s 0
Digits[6] = ' '; // Si ho ?s, hi posem un espai
if (Digits[5] == '0') { // Mirem si el segon d?git ?s 0
Digits[5] = ' '; // Si ho ?s, hi posem un espai
if (Digits[4] == '0') { // Mirem si el tercer d?git ?s 0
Digits[4] = ' '; // Si ho ?s, hi posem un espai
if (Digits[3] == '0') { // Mirem si el quart d?git ?s 0
Digits[3] = ' '; // Si ho ?s, hi posem un espai
if (Digits[2] == '0') { // Mirem si el cinquè d?git ?s 0
Digits[2] = ' '; // Si ho ?s, hi posem un espai
if (Digits[1] == '0') { // I mirem si ho ?s el sisè
Digits[1] = ' '; // Si ho ?s, hi posem un espai
}
}
}
}
}
}
for (int k = 6; k >= 0; k--){ // Escrivim set valors
send_char(Digits[k]); // Escriu un d?git a la pantalla
}
}
void write_bar(unsigned int Lvl) {
if (Lvl >= 100) {
Lvl = 80; // Com a màxim 80
} else {
Lvl = Lvl*8/10; // Disposem de 80 nivells en lloc de 100
}
while (Lvl >= 5) { // Mentre haguem d'escriure barres senceres
send_char(5); // Dibuixem un rectangle de la barra
Lvl -= 5; // Descomptem el que hem dibuixat
}
if (Lvl != 0) {
send_char(Lvl); // Dibuixa l'últim rectangle (parcial)
}
}
void send_char(char Caracter) {// Escriu un caràcter a la pantalla LCD
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
}
void clear_lcd(void) { // Esborra la pantalla LCD
send_char(254); // Caràcter de control
send_char(1); // Esborra la pantalla i posa el cursor a l'inici
}
void cursor(char row, char column) {
char pos = 0; // Variable per a calcular la posició
if (row == 2) {
pos = 64; // La primera columna de la segona fila és 64
}
if (column > 0 && column < 33) { // Comprovem que sigui un valor raonable
pos = pos + column; // Sumem les adreces
pos = pos - 1; // Restem 1 perquè numera des de 0
}
pos = pos + 128; // Posa el bit de posicionat a 1
send_char(254); // Control de la posició del cursor
send_char(pos); // 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 (int j = 5; j >= 0; j--){ // Hem d'enviar 6 bytes
for (int 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 clear_led(void) { // Apaga tots els LED
char Bytes[6]; // Els sis bytes que cal enviar
for (int 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
}
}
void def_char(char num, char val) {
send_char(254); // Caràcter de control
send_char(num*8 +64); // Posició
for (signed char k = 0; k < 7; k++){
send_char(val); // Envia línies
}
send_char(0b00000000); // L'última línia buida per evitar problemes
}

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