Medindo o Tempo de Uma Volta
A medição em si é simples: um contador (tickVolta) é zerado a cada começo de volta e incrementado pela interrupção periódica do timer. A parte mais complicada está em detectar o começo da volta, o que é feito pelo sensor de efeito Hall conectado ao ADC.
Como detalhei anteriormente, o ADC nos fornecerá um valor proporcional à proximidade do imã fixo ao quadro da bicicleta. Nos meus testes verifiquei um valor de 88 quando o sensor está longe do imã. Quando o sensor passa na frente do imã este valor aumenta ou diminui conforme o polo do imã que fica próximo do sensor.
Uma vez que o valor varia ligeiramente durante a passada, uso uma histerese (como detalhei aqui). O código abaixo faz a detecção:
- if (!bDetectou && ((medida > VPOS_1) || (medida < VNEG_1)))
- {
- // fim de uma volta
- bDetectou = TRUE;
- // pulando um trecho que explico depois...
- tickVolta = 0;
- P1OUT |= 1;
- }
- else
- {
- if (bDetectou && (medida < VPOS_2) && (medida > VNEG_2))
- {
- // pode procurar imã de novo
- bDetectou = FALSE;
- P1OUT &= 0xFE;
- }
- }
Após a detecção do imã é preciso esperar o valor retornar para perto da referência antes de detectar uma outra passagem do imã: (bDetectou && (medida < VPOS_2) && (medida > VNEG_2)).
Cálculo da Velocidade
Esta parte é trivial, desde que se preste atenção às unidades de medida. A velociadade é distância dividida por tempo e queremos exibir o resultado final em Km/h, com uma casa decimal.
A constante CIRCUNF é a circunferência da roda (quanto ela anda em uma volta), medida em milímetros. A variável tickVolta indica quantos ticks de 100 microsegundos demorou a volta.
A velocidade em Km/s corresponde a (CIRCUNF/1000000)/(tickVolta/10000). Para obtermos em Km/h precisamos multiplicar por 3600 (número de segundos por hora). Por último vamos multiplicar por 10 para termos um casa decimal (mais precisamente, estamos medindo a velocidade em unidades de 0,1Km/h). Simplificando as potências de dez, ficamos com:
- // Calcula a velocidade em 0,1 Km/h
- // veloc = distância / tempo
- // CIRCUNF em mm, ticVolta em 100 us
- // veloc = 3600 * (CIRCUNF / 100000) / (ticVolta / 10000)
- veloc = (unsigned int) ((CIRCUNF*360L)/tickVolta);
Para a apresentação do resultado, dividimos o tempo de uma volta em frames. Cada coluna de cada caracter do resultado será apresentada por um frame. Entre um caracter e outro teremos SPACE frames. A variável tickFrame contem quantos ticks dura cada frame; contFrame conta os ticks até o final do frame atual. Inicialmente contFrame contém a quantidade de ticks que precisam ser aguardados até a posição inicial da mensagem (escolhida para garantir a legibilidade da mensagem).
Uma dificuldade é que a apresentação é feita na volta seguinte a cada medição de velocidade. Se a bicicleta estiver acelerando ou freando a apresentação fica muito ruim. Após algumas tentativas mal sucedidas de compensar isto, acabei optando por não apresentar a mensagem quando ocorre uma mudança grande de velocidade entre duas voltas.
Dada a posição onde eu montei o Spoke-o-dometer e o sentido de rotação da roda, é necessário apresentar a mensagem do fim para o começo. Ou seja da última coluna do último caracter para a primeira coluna do primeiro caracter. Por este motivo a mensagem é montada em ordem contrária e a variável col é iniciada com 5.
Segue o resto do código que é executado quando a roda completa uma volta:
- // Calcula variação em relação à volta anterior
- if (tickVolta > tickVoltaAnt)
- delta = tickVolta - tickVoltaAnt;
- else
- delta = tickVoltaAnt - tickVolta;
- // Só mostra velocidade se maior que 7.0 Km/h
- // e não variou mais que 25%
- if ((veloc > 70) && (delta < (tickVolta >> 2)))
- tmsg = escreve_kmh(veloc, msg);
- else
- {
- msg[0] = ' ';
- msg[1] = 0;
- tmsg = 1;
- }
- pMsg = &msg[0];
- // Calcula o tempo por frame e a contagem para a posição inicial
- tickFrame = tickVolta / NFRAMES;
- contFrame = (tickVolta *(NFRAMES - POS_MSG - tmsg*(5+SPACE))) / NFRAMES;
- tickVoltaAnt = tickVolta;
- col = 5;
- // Formata a velocidade para apresentação
- // Mensagem é montada em ordem reversa
- // "h/mk#,###"
- int escreve_kmh (unsigned int vel, char *pv)
- {
- int tam = 7;
- *pv++ = 'h';
- *pv++ = '/';
- *pv++ = 'm';
- *pv++ = 'k';
- *pv++ = '0' + (vel % 10); vel /= 10;
- *pv++ = ',';
- *pv++ = '0' + (vel % 10); vel /= 10;
- if (vel != 0)
- {
- *pv++ = '0' + (vel % 10); vel /= 10;
- tam++;
- if (vel != 0)
- {
- *pv++ = '0' + (vel % 10);
- tam++;
- }
- }
- *pv = 0;
- return tam;
- }
- // Gerador de Caracteres
- // Cada caracter corresponde a 5 bytes
- // Cada byte corresponde a uma coluna
- static const byte gc[16][5] =
- {
- 0x7C, 0x82, 0x82, 0x82, 0x7C, // 0
- 0x02, 0x42, 0xFE, 0x02, 0x02, // 1
- 0x46, 0x8A, 0x92, 0xA2, 0x42, // 2
- 0x44, 0x82, 0x92, 0x92, 0x6C, // 3
- 0xF0, 0x10, 0x10, 0x10, 0xFE, // 4
- 0xF2, 0x92, 0x92, 0x92, 0x8C, // 5
- 0x6C, 0x92, 0x92, 0x92, 0x0C, // 6
- 0x80, 0x86, 0x98, 0xA0, 0xC0, // 7
- 0x6C, 0x92, 0x92, 0x92, 0x6C, // 8
- 0x60, 0x92, 0x92, 0x92, 0x6C, // 9
- 0x00, 0x00, 0x00, 0x00, 0x00, // SPACE (10)
- 0x00, 0x04, 0x06, 0x00, 0x00, // , (11)
- 0x7E, 0x08, 0x14, 0x22, 0x00, // k (12)
- 0x1E, 0x10, 0x0C, 0x10, 0x1E, // m (13)
- 0x03, 0x0C, 0x18, 0x30, 0x60, // / (14)
- 0x7E, 0x08, 0x08, 0x06, 0x00 // h (15)
- };
- if (contFrame == 0) // fim do frame atual
- {
- if (col > 0)
- {
- // vamos para a coluna anterior
- col--;
- valor = gc [char_index(*pMsg)][col];
- P1OUT &= 0xC1;
- P1OUT |= (valor & 0x3E);
- P2OUT &= 0x3F;
- P2OUT |= (valor & 0xC0);
- contFrame = tickFrame;
- }
- else
- {
- // passar ao caracter seguinte
- P1OUT = P1OUT & 0xC1;
- P2OUT = P2OUT & 0x3F;
- col = 5;
- pMsg++;
- if (*pMsg == 0)
- tickFrame = 0; // fim da mensagem
- else
- contFrame = tickFrame * SPACE;
- }
- }
- else
- contFrame--;
- / c_index: retorna o índice do caractere na matriz gc
- int char_index(char c)
- {
- if ((c - '0') >= 0 && (c - '0') < 10)
- return (c - '0');
- if (c == ' ') return CHAR_SPACE;
- if (c == ',') return CHAR_COMMA;
- if (c == 'k') return CHAR_K;
- if (c == 'm') return CHAR_M;
- if (c == '/') return CHAR_SLASH;
- if (c == 'h') return CHAR_H;
- //else (caractere estranho)
- return CHAR_SLASH;
- }
Com isto acredito ter coberto todo o código. Em caso de dúvida, o formulário de comentários está logo abaixo.
Nenhum comentário:
Postar um comentário