quinta-feira, junho 25, 2009

Notícias Curtas de uma Manhã Chuvosa

Nesta manhã chuvosa em Sampa, enquanto crio coragem para começar mais um dia de trabalho, listo algumas notícias curtas mas interessantes.

Firmware Bloqueia Uso de Baterias Não Originais em Câmeras

Já escutei sobre esquemas de proteção para cartuchos e toners de impressora, mas para bateria de máquina fotográfica é novidade para mim. A Panasonic publicou atualizações de firmware que impedem o uso de baterias não originais, "para maior segurança aos usuários". E também aos acionistas.

Link: http://www.reghardware.co.uk/2009/06/25/panasonic_battery_drm/

Novo Anti-Virus Gratuito da Microsoft é Bom, Mas Download do Beta Está Suspenso

A Microsoft anunciou o beta de um novo anti-virus, o Security Essentials. As primeiras análises foram positivas; parece ser uma boa opção para quem está cansado dos paquidermes que viraram a maioria dos anti-virus e procura algo mais simples. Entretanto a Microsoft suspendeu o download um dia após o anúncio, provavelmente já satisfeita com a quantidade de downloads.

Links:
http://www.theregister.co.uk/2009/06/24/ms_morro_review/
http://www.theregister.co.uk/2009/06/24/mse_beta_halt/

Mais Uma Tecnologia Microsoft Para Usuários "Tarados"

Na semana passada eu brinquei que a Microsoft tinha achado um novo mercado com o tratamento de vídeos no Bing e com o modo InPrivate do IE8. O recurso DeepZoom do Silverlight também está atraindo os membros deste mercado.

Link (NSFW*): http://www.meiobit.com/meio-bit/miscelaneas/microsoft-deepzoom-versao-brasileira

Id Software Não é Mais Independente

A empresa Id Software (precisa mencionar os jogos dela?) foi vendida para uma distribuidora e deixa de ser o exemplo mais visível e bem sucedido de desenvolvedor independente. As duas empresas garantem que nada vai mudar, mas John Carmack trocou o seu tradicional tom técnico para algumas frases que lembram tirinhas do Dilbert:

"We will now be able to grow and extend all of our franchises under one roof, leveraging our capabilities across multiple teams while enabling forward looking research to be done in the service of all of them."

Link: http://www.theregister.co.uk/2009/06/24/id_software_sells_to_bethesda/


* NSFW = Not Safe For Work - contém imagens não apropriadas para o ambiente de trabalho. Ou na frente de crianças ingênuas.

quarta-feira, junho 24, 2009

Diploma Obrigatório

A polêmica em torno do fim da obrigatoriedade de diploma para os jornalistas reacende uma discussão mais abrangente: a importância ou não de um diploma para exercer uma determinada profissão.

É comum vermos nas discussões duas posições que eu classifico como enganos: a de que um diploma "dá direito" a um emprego e a de que certas profissões não requerem conhecimentos específicos. O primeiro engano é reforçado por instituições com interesses econômicos no assunto, como cursos preparatórios e faculdades. O segundo é um argumento usado por quem quer desmerecer uma profissão ou não está disposto ao esforço de preparação e está procurando um atalho.

O Que Um Diploma Não Garante

Para bem ou para o mal, obter um diploma atualmente pode ser um tarefa fácil. Com a proliferação das faculdades e cursos não presenciais, a disponibilidade de vagas está superando a procura em várias profissões, exorcizando o fantasma do vestibular.

Ao contrário que alguns apregoam, um diploma não é uma garantia de uma pessoa está apta a ser um bom profissional. Em muitos casos, nem mesmo confirma que a pessoa conseguiu absorver um mínimo do que foi (ou devia) ser ensinado. E é claro que existem muitos outros requisitos para ser um bom profissional, como experiência e ética.

O Que Uma Boa Faculdade Pode Fornecer

Embora não sejam poucos os casos de auto-didatas bem sucedidos, aprender uma profissão estudando sozinho não é trivial.

Em primeiro lugar, uma faculdade deve ter um currículo organizado de forma a colocar os estudos em uma ordem apropriada e com tempos proporcionais à importância e a dificuldade.

As boas faculdades costumam focar mais na base que nos detalhes. Isto prepara pessoas mais flexíveis porém requer um esforço delas para se preparar para a prática.

Bons professores vão ajudar a entender as partes mais complicadas e incentivar o estudo.

A faculdade pode também fornecer uma infraestrutura composta de laboratórios, bibliotecas, etc.

Por último, mas não menos importante, uma faculdade pode fornecer um ambiente propício ao estudo e à troca de informações com pessoas com o mesmo interesse.

O Que Um Diploma Pode Significar Para Quem Está Contratando

É claro que isto varia de pessoa a pessoa. Existem alguns que consideram um diploma um requisito essencial. Existem outros que dão um grande peso a diplomas de determinadas instituições (muitas vezes àquela em que ele próprio estudou).

Do meu ponto de vista, um diploma é um sinal que a pessoa teve o interesse e a oportunidade de aprender mais sobre a profissão pela qual se interessa. Para confirmar este sinal e que a oportunidade foi bem aproveitada é preciso olhar além do diploma.

Exemplos e Contra-Exemplos

Quase todos concordam que profissões como médico e engenheiro civil devem exigir diploma. Por outro lado, reconhecem que isto não elimina erros médicos nem construções inseguras.

Uma profissão que exige diploma é a de secretária. É bem provável que a maioria das "secretárias" que você conheça estejam registradas como "assistentes administrativas". Este "jeitinho" burla a exigência, mas a maioria destas "secretárias" não tem conhecimento das técnicas desta profissão.

Exigindo Mais Que Um Diploma

Para várias profissões existem Conselhos Regionais e o exercício da profissão está vinculado ao registro do profissional ao respectivo conselho. Na maioria dos casos o registro requer a apresentação do diploma, preenchimento de uma ficha e o pagamento de uma anuidade. Os conselhos tem a autoridade de cassar o registro dos maus profissionais, mas isto não é frequente.

A exceção mais conhecida são os advogados, que submetendo os candidatos a um exame. As taxas de reprovação costumam ser altas, reforçando a ideia que o diploma não assegura o conhecimento.

A Situação da Informática

A área de informática, talvez por ser relativamente nova, não tem maiores exigências nas suas profissões, sendo comum se encontrar pessoas com as diversas formações (e mesmo ausência de formação).

Se por um lado isto significa o livre acesso à área, por outro lado existe um número elevado de profissionais com conhecimentos deficientes, perpetuando mitos e produzindo trabalhos de baixa qualidade.

Concluindo

Acredito que a exigência de diploma para determinadas profissões não irá barrar profissionais ruins nem bons, já que ambos não terão dificuldades em obter um diploma. Inescrupulosos encontrarão maneiras de exercer a profissão ilegalmente, seja mentindo descaradamente, criando "nomes fantasia" para a profissão ou se escondendo por traz de um registro "de aluguel".

O que eu acho importante são duas coisas: a preocupação do profissional com o domínio das teorias e técnicas da sua profissão e a responsabilidade pelo trabalho produzido.

sexta-feira, junho 19, 2009

Observações Curtas de Sexta

Seguem abaixo algumas observações curtas sobre notícias que li esta semana.

Windows Seven sem IE na Europa

A notícia (ameaça?) de que a Microsoft vai lançar o Windows Seven na Europa sem o IE, continua causando alvoroço. A Opera Software, principal instigadora da investigação da UE sobre o bundling do IE no Windows, continua exigindo que durante a instalação o Windows apresente um menu com várias alternativas de navegador, o que a Microsoft considera o equivalente a exigir que a Coca Cola coloque uma lata de Pepsi nos seus six-pack.

Na minha opinião a gritaria reforça a posição da Microsoft de que os usuários esperam que um sistema operacional atual venha com um browser incluso.

Links:

http://www.channelregister.co.uk/2009/06/11/microsoft_windows_ie_sku_europe/
http://www.channelregister.co.uk/2009/06/15/jcxp_calls_for_opera_browser_boycott/

Opera "Reinventa" a Web

Após anunciar que ia reinventar a Web, a Opera anunciou o Opera Unite, que é uma junção de Web Server com Web Browser. O objetivo é permitir pessoas a compartilharem conteúdo de uma forma (quase) descentralizada.

Para mim (e para o The Register) parece ser um tentativa de contra-atacar os investimentos da Microsoft e Google na "Cloud Computing". Mas não me parece uma boa idéia termos milhares de web servers espalhados na mão de usuários que não querem nem sabem gerenciá-los. Nem ter toda esta infraestrutura amarrada por servidores da Opera (responsáveis pelo roteamento, já que a maioria das máquinas executando browsers não tem um IP fixo na internet).

Vamos ver também o que a RIAA vai falar sobre a funcionalidade decompartilhar música....

Link:

http://www.theregister.co.uk/2009/06/16/opera_unite/

Dias Difíceis para os Desenvolvedores para iPhone

O Worldwide Developers Conference da Apple gerou muitas notícias, particularmente o lançamento do iPhone 3GS. Entretanto nem tudo são flores: alguns desenvolvedores não estão nada satisfeitos com o tratamento dispensado pela Apple. Nos comentários no link abaixo, uma coisa rara hoje em dia: elogios para a Microsoft

Link:

http://www.osnews.com/story/21678/WWDC_quot_A_Giant_Middle_Finger_to_iPhone_Developers_quot_

Relacionado aos Dois Itens Anteriores?

Não é bom para as formigas ficarem no caminho dos elefantes. Da mesma forma, uma empresa pequena terá problemas se ficar no roadmap de uma maior. Fornecer um complemento para uma plataforma pode ser um negócio viável, mas pode ter vida curta.

Link:

http://www.joelonsoftware.com/items/2009/06/10c.html

Um Novo Mercado para Browsers e Buscadores?

Acidentalmente ou não, a Microsoft achou um "novo" mercado. Primeiro foi o modo InPrivate do IE8. Depois foi o modo como o Bing trata vídeos, o que permite burlar certos controles e restrições. Agora o Bing tem até um domínio específico para este "mercado alvo".

Link:

http://www.clicrbs.com.br/especial/rs/tecnologia/19,0,2547165,Microsoft-cria-dominio-de-buscas-especifico-para-pornografia.html


quarta-feira, junho 17, 2009

Sopke-o-dometer - Parte 11

Neste final da série vamos ver a lógica da aplicação em si. Temos três grandes partes: medir o tempo de uma volta da roda, calcular a velocidade correspondente e apresentar nos LEDs este resultado.

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;
}
}
O teste (!bDetectou && ((medida > VPOS_1) || (medida < VNEG_1))) verifica se nos afastamos da referência num momento em que ainda não detectamos o imã, indicando o início da passagem.

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);
Apresentação do Resultado

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;
A formatação da mensagem é feita por uma conversão "caseira" do número binário em caracteres:
// 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;
}
O gerador de caracteres, que indica qual LED deve estar aceso em cada coluna de cada caracter está em uma matriz de bytes:
// 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)
};
Sobra apenas o trabalho de mudar os LEDs ao final de cada frame, na interrupção do timer:
                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--;
A rotina char_index devolve o indice no gerador de caracteres para cada caracter:
/ 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.

segunda-feira, junho 15, 2009

Spoke-o-dometer - Parte 10

Neste post vou explicar a parte do código do Spoke-o-dometer (SOD) relacionada à programação dos periféricos do microcontrolador. A documentação da Texas a respeito está disponível aqui em formato PDF. Sigo abaixo a mesma ordem que a documentação.

A maior dificuldade nesta programação está na grande quantidade de opções disponíveis. Para cada módulo do microcontrolador é necessário escolher a opção mais apropriada, ver quais as opções default após o reset e alterar os bits necessários nos registradores que controlam as opções.

Clock

O MSP430F2013 que estou usando permite usar até três fontes para o clock:
  • LFXT1CLK: clock de baixa frequência gerado por um cristal externo. Não usado no SOD.
  • VCLOCK: clock interno de 12KHz. Usei no início para gerar as interrupções de tempo; na versão final não é usado.
  • DCOCLK: clock interno de alta frequência. Programado para 8MHz no SOD.
Estas fontes podem ser conectadas a três sinais de clock que alimentarão o processador e os periféricos. Esta conexão pode ser direta ou através de um divisor. Os sinais de clock são:
  • MCLK: master clock, alimenta o processador. No nosso caso, é o SCOCLK (8MHz).
  • SMCLK: sub-main clock, é uma das alternativas de clock para os periféricos. No SOD o SMCLK é configurado para DCOCLK/8 (1MHz) e será usado para o timer e para o conversor analógico digital (ADC).
  • ACLK: clock auxiliar (normalmente de baixa frequência). No SOD é configurado para VCLOCK e era usado inicialmente com o timer. Na versão final não é usado.
Os clocks estão diretamente ligados ao consumo de energia. Quando o processador está rodando, todos os clocks estão ativos e o consumo é máximo. O processador pode ser colocado para dormir aguardando interrupções; o MCLK é desligado e os demais clocks podem ser desligados se não estiverem ligados aos periféricos que irão acordar o processador.

Nos testes iniciais eu estava usando um gerenciamento de energia mais sofisticado. O SMCLK só era mantido ligado com o processador dormindo quando o ADC estava ligado. Quando o ADC estava inativo somente ACLK ficava ligado, deixando o consumo muito próximo de zero.

Na versão final o timer está sendo alimentado pelo SMCLK portanto estou deixando SMCLK e ACLK sempre ligados.

A programação dos clocks é feita nas linhas abaixo:
   // Altera a configuração de clock para ativar o VLOCLK
BCSCTL3 |= LFXT1S1;

// SMCLK = MCLK / 8
BCSCTL2 = DIVS_3;

// Alimentação já deve estar estável, vamos ligar o DCO
BCSCTL1 = CALBC1_8MHZ;
DCOCTL = CALDCO_8MHZ;
A macro abaixo coloca o processador no modo 0 de economia de energia, no qual o processador fica parado mas os clocks SMCLK e ACLK ficam ligados:
    LPM0;             // Dorme tratando interrupção
O controle da economia de energia fica no registrador de status. Quando uma interrupção ocorre, este registrador é salvo na pilha e todos os clocks são ligados. Ao encerrar o tratamento de uma interrupção o registrador de status é restaurado da pilha; se nada especial for feito o processador volta a dormir no mesmo modo que estava antes. Isto é suficiente para a versão final do código.

Nas versões de teste, onde eu mudava dinamicamente de modo de economia de energia, era preciso usar as macros _BIC_SR_IRQ e _BIS_SR_IRQ para mudar a imagem do registrador na pilha.

E/S Digital

Esta parte é simples. o MSP430F2013 possui 10 pinos que podem ser usados para entrada e saída digital. Oito deles compõem o chamado P1 e dois deles o P2 (que usa somente os dois bits mais significativos).

Em P1 os bits 0 a 5 são programados para saída e correspondem a LEDs (o bit 0 é o LED verde na placa do processador; os demais são LEDs vermelhos do SOD). Os bits 6 e 7 são as entradas do ADC.

Em P2 os bits 6 e 7 são programados para saída e correspondem a LEDs do SOD.

A programação fica assim:

// Programa entradas e saídas
P1SEL = 0xC0; // P1.0 a P1.5 -> LEDs
// P1.6 e P1.7 são A3+ e A3-
P1DIR = 0x3F;
P1OUT = 0; // Todos as saídas em zero

P2SEL = 0; // Todos os pinos como I/O
P2DIR = 0xFF; // Todos os pinos como saída
P2OUT = 0; // Todos as saídas em zero

PxSEL indica quais pinos serão de E/S digital (bit em 1) e quais serão usados para funções especiais (bit em 0). PxDIR indica se o pino é de entrada (bit em 0) ou saída (bit em 1). PxOUT controla os pinos programados para saída digital.

Watchdog Timer

O WDT é uma segurança contra programas perdidos: quando ativo o software precisa periodicamente re-armá-lo para o processador não ser reiniciado. Após um reset o WDT é ligado; no SOD estamos simplesmente desligando-o:
    // Desliga Watchdog
WDTCTL = WDTPW + WDTSSEL + WDTHOLD;
Timer

O MSP430F2013 possui um timer bastante versátil. No SOD vamos usá-lo somente para gerar uma interrupção periódica.

Isto é feito colocando-o no up mode: o timer conta de zero até um valor final (inclusive), gera a interrupção e volta a contar a partir de zero.

Uso como clock para o timer o SMCLK (1MHz) e programo o valor final em 99. Desta forma, uma interrupção é gerada a cada 100 pulsos do clock, o que resulta em um intervalo de 100 micro-segundos:

// Valor para contar 100us c/ clock de 1MHz
#define TEMPO_100uS 99 // (100us * 1MHz) - 1

// Programar a interrupção de tempo real p/ cada 100us
TACCR0 = TEMPO_100uS;
TACTL = TASSEL_2 + MC_1 + TAIE; // SCLK, up mode, interrupt

A rotina de tratamento da interrupção deve ser marcada através do pragma vector=TIMERA1_VECTOR. O timer pode gerar interrupções por diversos motivos, o registrador TAIV informa qual. O que nos interessa é o overflow:
// Tratamento da interrupção do Timer A
#pragma vector=TIMERA1_VECTOR
__interrupt void Timer_A_TO(void)
{
byte valor;

switch (TAIV)
{
case 10: // overflow
// nosso tratamento
break;

case 2: // CCR1, não utilizado
break;
}
}
Conversor Analógico Digital SD-16

Expliquei em detalhes o SD-16 na parte 5. A única alteração é que na versão final o DCO está a 8MHz mas o SMCLK continua sendo 1MHz. Em algumas versões intermediárias usei o SMCLK a 8MHz e programaei o SD-16 para dividí-lo por 8.

O código permanece assim:
    // Programa o ADC
// MSP430F2013 -> SD16
SD16CTL = SD16VMIDON + SD16REFON + SD16DIV_0 + SD16SSEL_1; // 1.2V ref, SMCLK
SD16INCTL0 = SD16INCH_3; // PGA = 1x, Diff inputs A3- & A3+
SD16CCTL0 = SD16SNGL + SD16UNI + SD16IE; // Single conversion, Unipolar, 256 SR, Int enable
SD16CTL &= ~SD16VMIDON; // VMID off: used to settle ref cap
SD16AE = SD16AE6 + SD16AE7; // P1.6 & P1.7: A3+/- SD16_A inputs
O SD16 é disparado de dentro da interrupção de tempo:
    if (!bAmostrando)
{
SD16CTL |= SD16REFON; // Liga a referência do SD16
SD16CCTL0 |= SD16SC; // Inicia uma conversão
bAmostrando = TRUE;
}

Quando a conversão é concluída, uma interrupção é gerada e o resultado está em SD16MEM0. Para economia de energia mantemos a referência ligada somente durante a conversão.
// Tratamento da interrupção do SD16
#pragma vector = SD16_VECTOR
__interrupt void SD16ISR(void)
{
unsigned int medida;

SD16CTL &= ~SD16REFON; // Desliga referência do SD16_A
medida = SD16MEM0 >> 8; // Pega medida (limpa IFG)
bAmostrando = FALSE; // Fim da amostragem

// trata a medida obtida
}
Embora o conversor forneça um resultado de 16 bits, usamos apenas os 8 mais significativos.


Com isto espero ter explicado toda a parte do código relacionada à programação dos periféricos do microcontrolador. No último post da série vou explicar a lógica do velocímetro.

sábado, junho 13, 2009

Spoke-o-dometer - Parte 9

Este post e os dois seguintes concluem esta série sobre um velocímetro que vai montado diretamente no aro da bicicleta e apresenta mensagens se valendo da persistência da visão. Neste post vou mostrar o hardware e o código final; no post seguinte vou explicar a programação dos periféricos do microcontrolador e no último explicar a lógica usada para calcular e apresentar a velocidade.

O Hardware

O projeto de hardware resistiu inalterado desde o começo. Repetindo o circuito (clique para ampliar):

A foto abaixo mostra a montagem:


O Software

Segue abaixo a listagem completa do software, que vou explicar nos post seguintes.

/*
Spoke-o-dometer
Teste 5 - Versão Completa

Daniel & Flávio Quadros - abril/junho, 2009
*/

#include <msp430.h>

// Constantes lógicas
#define FALSE 0
#define TRUE 1

// Tipo byte
typedef unsigned char byte;

// Circunferência aprox. da roda em milimetros
#define CIRCUNF 2090L

// Valor para contar 100us c/ clock de 1MHz
#define TEMPO_100uS 99 // (100us * 1MHz) - 1

// 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)
};

#define CHAR_SPACE 10;
#define CHAR_COMMA 11;
#define CHAR_K 12;
#define CHAR_M 13;
#define CHAR_SLASH 14;
#define CHAR_H 15;


// Limites para detectar passagem pelo ímã
#define VREPOUSO 88 // 01011000
#define VPOS_1 120 // 01111000
#define VPOS_2 100 // 01111000
#define VNEG_1 56 // 00111000
#define VNEG_2 76 // 00111000

static byte bAmostrando = FALSE; // TRUE enquanto aguarda ADC
static byte bDetectou = FALSE; // TRUE qdo detecta o imã


// Contagem do tempo de uma volta e
// controle da escrita da mensagem

#define NFRAMES 360 // frames por volta
#define POS_MSG 45 // inicio da mensagem
#define SPACE 3 // frames de espaço entre letras

static unsigned long tickVolta; // número de ticks em uma volta
static unsigned long tickVoltaAnt; // número de ticks da volta anterior
static unsigned long tickFrame = 0; // número de ticks por frame
static unsigned long contFrame; // ticks até o próximo frame
static byte col; // coluna sendo apresentada
static char *pMsg; // caracter sendo apresentado
static char msg[16]; // mensagem a apresentar

// Rotinas locais
int escreve_kmh (unsigned int vel, char *pv);
int char_index (char c);

// Programa Principal
void main (void)
{
unsigned int cont = 0;

// Desliga Watchdog
WDTCTL = WDTPW + WDTSSEL + WDTHOLD;

// Altera a configuração de clock para ativar o VLOCLK
BCSCTL3 |= LFXT1S1;

// SMCLK = MCLK / 8
BCSCTL2 = DIVS_3;

// Programa entradas e saídas
P1SEL = 0xC0; // P1.0 a P1.5 -> LEDs
// P1.6 e P1.7 são A3+ e A3-
P1DIR = 0x3F;
P1OUT = 0; // Todos as saídas em zero

P2SEL = 0; // Todos os pinos como I/O
P2DIR = 0xFF; // Todos os pinos como saída
P2OUT = 0; // Todos as saídas em zero

// Programa o ADC
// MSP430F2013 -> SD16
SD16CTL = SD16VMIDON + SD16REFON + SD16DIV_0 + SD16SSEL_1; // 1.2V ref, SMCLK
SD16INCTL0 = SD16INCH_3; // PGA = 1x, Diff inputs A3- & A3+
SD16CCTL0 = SD16SNGL + SD16UNI + SD16IE; // Single conversion, Unipolar, 256 SR, Int enable
SD16CTL &= ~SD16VMIDON; // VMID off: used to settle ref cap
SD16AE = SD16AE6 + SD16AE7; // P1.6 & P1.7: A3+/- SD16_A inputs

// Dá um tempinho para estabilizar a alimentação
while (cont < 0xFF)
cont++;
cont = 0;

// Alimentação já deve estar estável, vamos ligar o DCO
BCSCTL1 = CALBC1_8MHZ;
DCOCTL = CALDCO_8MHZ;

// Programar a interrupção de tempo real p/ cada 100us
TACCR0 = TEMPO_100uS;
TACTL = TASSEL_2 + MC_1 + TAIE; // SCLK, up mode, interrupt

// O nosso programa principal vai ficar dormindo,
// todo o tratamento será feito na interrupção
_BIS_SR(GIE);
LPM0; // Dorme tratando interrupção

// Nuca vai chegar aqui
while (1)
;
}

// Tratamento da interrupção do Timer A
#pragma vector=TIMERA1_VECTOR
__interrupt void Timer_A_TO(void)
{
byte valor;

switch (TAIV)
{
case 10: // overflow
tickVolta++; // mais um tick na volta atual
if (tickFrame != 0)
{
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--;
}
if (!bAmostrando)
{
SD16CTL |= SD16REFON; // Liga a referência do SD16
SD16CCTL0 |= SD16SC; // Inicia uma conversão
bAmostrando = TRUE;
}
break;

case 2: // CCR1, não utilizado
break;
}
}

// Tratamento da interrupção do SD16
#pragma vector = SD16_VECTOR
__interrupt void SD16ISR(void)
{
unsigned int medida;
unsigned int veloc;
unsigned long delta;
int tmsg;

SD16CTL &= ~SD16REFON; // Desliga referência do SD16_A
medida = SD16MEM0 >> 8; // Pega medida (limpa IFG)
bAmostrando = FALSE; // Fim da amostragem

if (!bDetectou && ((medida > VPOS_1) || (medida < VNEG_1)))
{
// fim de uma volta
bDetectou = TRUE;

// Calcula variação em relação à volta anterior
if (tickVolta > tickVoltaAnt)
delta = tickVolta - tickVoltaAnt;
else
delta = tickVoltaAnt - tickVolta;

// 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);

// 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;
tickVolta = 0;
col = 5;
P1OUT |= 1;
}
else
{
if (bDetectou && (medida < VPOS_2) && (medida > VNEG_2))
{
// pode procurar imã de novo
bDetectou = FALSE;
P1OUT &= 0xFE;
}
}
}

// 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;
}

// 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;
}
O Resultado

O vídeo abaixo mostra o Spoke-o-dometer em funcionamento:

sexta-feira, junho 12, 2009

Música na Wikipedia e YouTube

Peguei a mania de ao escutar música no micro sair procurando o artista ou a música no Wikipedia e no YouTube.

Normalmente a Wikipedia possui uma boa biografia de músicos e bandas. A quantidade de informações sobre álbuns varia bastante. Páginas para músicas específicas são mais raras e poucas contem informação relevante.

O YouTube é bastante divertido. Apesar da qualidade dos vídeos ser baixa na maioria dos casos, dá para encontrar uma quantidade imensa de curiosidades (pelo menos enquanto os donos dos direitos autorais não partirem para a briga). No meu Firefox tenho o YouTube na lista de buscadores e a extensão DownloadHelper para salvar os vídeos.

Comecei a usar recentemente o "tocador" aTunes que apresenta informações sobre o artista obtidas na Last.Fm e vídeos relacionados no YouTube. Futuramente faço uma crítica mais completa deste software.

Aqui seguem os links para algumas curiosidades que achei no YouTube:

Genesis - Rock Progressivo
Peter, Paul and Mary - Folk
Roling Stones
  • Paint It Black, data ignorada mas com a banda bem novinha e com Brian Jones
Diana Krall - Jazz & Blues
  • Crazy, com o maridão Elvis Costello e a figuraça Willie Nelson.
Allman Brothers - Southern Rock
O YouTube é também um bom lugar para quem quer aprender a tocar:
E guardei para o fim uma pequena joia. A banda Focus era bem conhecida na minha juventude, particularmente o álbum Moving Waves que contem a indescritível Hocus Pocus. Para ouvir nas caixas, não no fone de ouvido.

Editado às 10:15: problemas de formatação no final do texto, que não aparecem no editor do Blogger. O motivo eram uns tags <span>. Já não basta os problemas com spam, agora tenho que me preocupar com span!

Editado novamente em 11/nov/09: corrigido um erro no texto e acertado o parágrafo anterior (pois o editor do Blogger considerou o tag span literalmente).

quarta-feira, junho 10, 2009

Spoke-o-dometer - Parte 8

Tendo passado mais de um mês desde o post anterior da série, me parece apropriado dar alguma notícia. Mais que o bug no compilador, falta de tempo e foco é o que impediu de concluir este projeto.

O meu filho avançou no código, colocando o cálculo e a apresentação da velocidade em km/h. Durante esta mexida dele, deu para perceber algumas limitações e erros do código apresentado anteriormente.

O código é movido a uma interrupção periódica de timer, que era gerada a partir de um clock interno de 12KHz. O primeiro problema é que eu estava calculando errado o valor de contagem (TEMPO_xxx). Após detectar divergência com uma leitura de velocidade na base do cronometro, revi com atenção a documentação do MSP430 e percebi que ele conta um a mais que o valor programado. Assim, uma contagem de 6 gera uma temporização de 583 useg ao invés de 500 useg.

Um problema mais crítico é que à medida que a velocidade sobe temos menos ticks por volta. Na hora de dividir a volta em "frames" a imprecisão aumenta, fazendo a largura dos caracteres variarem. A solução para isto vai ser usar um clock mais alto; isto aumentará o consumo do circuito mas dará uma precisão muito maior. Isto exige rever a programação do hardware e fazer alguns cálculos para garantir que os números vão caber nas variáveis.

Por último, estou usando o tempo de uma volta para apresentar a mensagem na volta seguinte, o que causa problemas quando estamos acelerando ou freando. Para melhorar isto pretendo considerar também a variação de tempos de uma volta para outra.

Tudo isto exige algum estudo, mexidas no código e testes. Estou contando com o feriado desta semana para chegar a uma versão "final", que vou publicar junto com uma tentativa mais decente de explicar o funcionamento do código.

terça-feira, junho 09, 2009

SDL Game Contest

Ainda na linha de competições de desenvolvimento de jogos, segue o link para uma competição de desenvolvimento de um "side-scrolling shooter" em C, C++ ou Delphi*, usando SDL:

http://www.sdltutorials.com/contest/

O prazo é até 1o de setembro de 2009 e o 1o prêmio é de US$200.

O organizador sugere como inspiração o jogo R-Type.

Fonte: GameDev.net

* ver os comentários após o anúncio.

sábado, junho 06, 2009

15a Competição Anual de Ficção Interativa

Para quem quiser se arriscar a escrever uma aventura curta, o site da competição deste ano já está no ar: http://www.ifcomp.org.

As ferramentas mais usadas para escrever uma aventura (todas grátis) são:
Você pode também criar a sua própria ferramenta!

sexta-feira, junho 05, 2009

Últimas Notícias

Versão on-line de jornal invadida pela gang dos nulls:

terça-feira, junho 02, 2009

A Volta da Ilha do Macaco

Quando se fala em jogos de Aventura, AIlha do Macaco é sem dúvida um clássico. O criador da Ilha do Macaco, Ron Gilbert, colocou no seu blog uma lista de comentários e curiosidades sobre o jogo. Mais interessante ainda anunciou que: