En realitat es tracta d'un micròfon i un petit amplificador muntat en una placa, com aquest que permet ajustar l'amplificació del senyal.
[AF]El sensor es connecta amb tres fils: els d'alimentació (positiu, VCC, i negatiu, GND) i el de senyal que connectarem a una entrada analògica.
El sensor ens dona una lectura proporcional al senyal de so, que és una ona. Atès que les entrades analògiques només admeten tensions positives, el sensor suma la meitat de la tensió d'alimentació al senyal de sortida. Això vol dir que en silenci llegirem un valor de 511 i quan hi ha so tindrem valors que oscil·laran al voltant d'aquest nombre. En el nostre cas connectarem el sensor a l'entrada AN8 (pota RC6). En la figura, tenim un exemple d'una ona de so que té la mitjana al voltant de 511 (com és esperable), un valor màxim de l'ordre de 780 i un valor mínim de l'ordre de 230.

Farem un programa de prova que ens permeti llegir 32 valors del sensor durant un temps de 20 ms. Els valors llegits els mostrarem a una pantalla LCD. Si dividim els 20 ms en 32 parts ens surt que cal llegir un valor cada 625 μs. Per comptar el temps farem servir el Timer 0. Ens interessa posar-li un factor de configuració que tingui el màxim nombre d'iteracions possible (per tenir la màxima precisió) però que sigui inferior a 256. La presselecció que candrà posar-hi serà la diferència entre 256 i el nombre d'iteracions. Veiem algun cas:
| Bits | Escala | Període | Iteracions | Preselecció |
| 111 | 1/256 | 256 μs | 2 | 254 |
| ... | ... | ... | ... | ... |
| 010 | 1/8 | 8 μs | 78 | 178 |
| 001 | 1/4 | 4 μs | 156 | 100 |
| 000 | 1/2 | 2 μs | 313 | --- |
Ens quedem amb el penúltim valor. El temporitzador partirà d'un valor de 100 i s'anirà incrementant cada 4 μs. Quan arribi a zero hauran passat 624 μs. Cada cop que el temporitzador acabi de comptar el reiniciarem, llegirem l'entrada analògica i guardarem el resultat en un vector. Llegirem 32 valors de dos bytes cada un; en total 64 bytes. Quan l'usuari premi el polsador, escriurem els 32 valors llegits a la pantalla. Inicialment els quatre primers a la primera línia (quatre caràcters per valor) i els quatre següents a la segona; amb això en tindrem 8. Quan premem el polsador n'escriurem 8 més i així fins que al quart cop ja els haurem escrit tots.
#pragma config FOSC = INTRCIO, WDTE = OFF, PWRTE = OFF, MCLRE = OFF, CP = OFF #pragma config CPD = OFF, BOREN = OFF, IESO = OFF, FCMEN = OFF #include <xc.h> // Carrega el fitxer de funcions necessari per al compilador XC8 #define _XTAL_FREQ 4000000 // La freqüència del rellotge és 4 MHz
unsigned int Lectura[32]; // Per guardar els valors llegits int pos = 0; // Punter del vector
// Definició de les funcions que farem servir void Linia(void); // Escriu una línia de resultats 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 // (filera 1 a 2 i columna 1 a 32, segons pantalla)
void main (void) {
OPTION_REG = 0b1000000; // Configuració de Timer0
// 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
RCSTAbits.SPEN = 1; // Activa comunicació sèrie
TXSTAbits.TXEN = 1; // Activa comunicació
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 C
__delay_ms(200); // Retard de 0,2 s
EnviaL('P'); // Lletra
EnviaL('r'); // Lletra
EnviaL('e'); // Lletra
EnviaL('m'); // Lletra
ADCON0 = 0b10100001; // Activa el conversor connectat a AN8
// amb el resultat justificat per la dreta
__delay_ms(400); // Retard de 0,4 s
TMR0 = 100; // Presselecció de 100, que són 156 iteracions
// (amb un factor 4 dona 624)
INTCONbits.T0IF = 0; // Desactivem el bit de final de temporització
// Per assegurar la desconnexió del programador
for (signed char k = 0; k < 32; 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;
Lectura[k] = Lectura[k] << 8;
Lectura[k] = Lectura[k] | ADRESL; // Guardem el resultat
}
// Anem a escriure a la pantalla
for (signed char k = 0; k < 4; k++){ // A cada pantalla 1/4 dels resultats
while (RA3 == 1) // Espera a que estigui premut
; // No fa res
__delay_ms(200); // Retard de 0,2 s
while (RA3 == 0) // Espera a que no estigui premut
; // No fa res
__delay_ms(200); // Retard de 0,2 s
Esborra(); // Esborra la pantalla;
Linia(); // Escriu una línia de resultats
Cursor(2, 1); // Posició
Linia(); // Escriu una línia de resultats
}
while (1)
; // Es bloqueja aquí
}
void Linia(void){
for (signed char k = 0; k < 4; k++){ // Escrivim quatre valors
escriuValor(Lectura[pos++]); // Escriu un valor a la pantalla
}
}
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
for (signed char j = 0; j < 5; j++){ // 5 dígits
Digits[j] = Valor % 10;
Valor = Valor / 10;
}
// Passem els dígits a ASCII
for (signed char j = 0; j < 5; j++){ // 5 dígits
Digits[j] = Digits[j] + '0'; // Li sumem el codi ASCII de 0
}
// Suprimim zeros innecessaris
char mira0 = 1; // De moment, hem de mirar si el caràcter és zero
for (signed char j = 4; j > 0; j--){ // El 0 de les unitats el mostrarem sempre
if(mira0 == 1) { // Encara toca comprovar sí és 0?
if(Digits[j] == '0') { // Mirem si el dígit és 0
Digits[j] = ' '; // Si ho és, hi posem un espai
} else {
mira0 = 0; // Si no ho és, ja no cal mirar-ne més
}
}
}
for (signed char k = 3; k >= 0; k--){ // Només escrivim quatre valors
EnviaL(Digits[k]); // Escriu un dígit a la pantalla
}
}
void EnviaL(char Caracter) {
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 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
}
Si volem conèixer el nivell de so actual caldrà tractar els valors llegits per trobar l'amplitud o el valor RMS del so. Una forma senzilla és llegir el senyal un cert nombre de vegades i guardar-se el valor més gran i el més petit. Acabada la lectura, l'amplitud serà la resta dels dos; llavors podrem tornar a mirar l'evolució del senyal.
Una ona de so pot tenir uns màxims molt acusats però de durada molt curta. Llavors ens donarà una amplitud gran però que no representa la potència del so. Una manera molt millor de mesurar el nivell del so és calcular el seu valor eficaç o RMS normalitzat. Per calcular el valor eficaç normalitzat necessitem calcular primer la mitjana:

I després calcularem el valor eficaç de la resta entre cada valor i la mitjana per tal que els valors estiguin normalitzats (centrats en el zero):


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