O meu filho resolver tentar dar uma mexida no código do Spoke-o-dometer que estou desenvolvendo. Para isto instalei o sistema de desenvolvimento no micro dele e copiei a versão mais recente do código (que está aqui). Como primeiro teste, ele compilou o programa do jeito que estava e gravou no microcontrolador. E os LEDs piscaram de forma incorreta.
Descobri então que a versão que instalei no micro dele é mais recente que a que está instalada no meu. Concluí então que eu tinha feito alguma besteira no código fonte, que por sorte funcionava no compilador antigo. Após examinar o código sem encontrar nada estranho, resolvi gerar a listagem do código fonte entremeado com o código assembly gerado nas duas versões, para ver se tinha alguma pista.
Acabei chegando à linha abaixo:
P1OUT = (P1OUT & 0xC1) | (valor & 0x3E);
Este é um código tipo de quem precisa "brincar" com os bits em um microcontrolador. P1OUT é um registrador que controla algumas das saídas digitais; um bit em 1 corresponde a uma saída em nível alto e um bit em 0 corresponde a uma saída em nível baixo. No meu hardware, os bits 0 a 5 controlam 5 LEDs, o bit 0 é um LED verde que uso para indicar a detecção do ima e os demais são usados para mostrar os caracteres. 'valor' contém a nova programação dos LEDs (a coluna atual do caracter a ser apresentado), nos bits 7 a 1. Para colocar esta nova programação é preciso trocar o valor dos bits 5 a 1 de P1OUT, sem alterar os demais. É isto que a expressão booleana acima faz:
Na versão antiga do compilador (MSP430 C/C++ Compiler V3.42A/W32) o código gerado é bastante simples:
MOV.B R15, R14 ; R14 recebe o valorJá a versão nova (IAR MSP430 C/C++ Compiler V4.11B/W32) gera esta pequena monstruosidade:
AND.B #0x3e, R14 ; R14 contem valor & 0x3E
MOV.B &0x21, R13 ; 0x21 é o endereço de P1OUT
AND.B #0xc1, R13 ; R13 contem P1OUT & 0xC1
BIS.B R14, R13 ; R13 recebe R13 | R14
MOV.B R13, &0x21 ; atualiza P1OUT
MOV.B R15, R14 ; R14 recebe o valorou seja, corresponde a algo como
CLRC
RRC.B R14 ; roda R14 para a direita
BIT.B #0x1, R14
JC ??Timer_A_TO_4 ; Desvia se (R14 & 0x01) != 0
BIC.B #0x2, &0x21 ; Desliga bit 1 de P1OUT
JMP ??Timer_A_TO_5
Timer_A_TO_4:
BIS.B #0x2, &0x21 ; Liga bita 1 de P1OUT
??Timer_A_TO_5:
if (valor & 0x02)Se alguém conseguir explicar porque este código foi gerado, eu agradeço. Por enquanto eu considero um bug do compilador.
P1OUT |= 2;
else
P1OUT &= 0xFD;
Para contornar este problema a solução foi quebrar a expressão em duas linhas:
P1OUT = (P1OUT & 0xC1);o que gera o bem razoável
P1OUT = P1OUT | (valor & 0x3E);
AND.B #0xc1, &0x21Reparar que a quebra em duas linhas causou duas escritas em P1OUT, o que não é problema neste caso. Se fosse problema, bastava montar o novo valor em uma variável auxiliar.
MOV.B R15, R14
AND.B #0x3e, R14
BIS.B R14, &0x21
4 comentários:
Olá, DQ.
Aparentemente, ele tentou criar um código mais simples e burro, mas parou na primeira "interação". Veja se existe alguma continuação abaixo. Se não existir, esse é o bug:
Vendo em binário, conclui-se que P1OUT zera cinco bits, começando do 1, e seta os mesmos cinco bits, de acordo com o valor de "valor", que só leva em conta os mesmos cinco bits:
P1OUT = (P1OUT & 11000001 ) | ( valor & 00111110 )
P1OUT = ( valor & 00000010 ) ?
P1OUT | 00000010
: P1OUT & 11111101
Isso me parece o início de um código que funcionaria, se ele tivesse feito a mesma instrução para cada um dos cinco bits:
P1OUT = ( valor & 00000010 ) ?
P1OUT | 00000010
: P1OUT & 11111101
P1OUT = ( valor & 00000100 ) ?
P1OUT | 00000100
: P1OUT & 11111011
P1OUT = ( valor & 00001000 ) ?
P1OUT | 00001000
: P1OUT & 11110111
P1OUT = ( valor & 00010000 ) ?
P1OUT | 00010000
: P1OUT & 11101111
P1OUT = ( valor & 00100000 ) ?
P1OUT | 00100000
: P1OUT & 11011111
Deve existir algum "motivo interno" para o compilador fazer esse tipo de código (flags de desempenho x tamanho do código, por exemplo).
[]s
Caloni,
Não tinha nenhuma continuação, era só isto mesmo. Está na lista fazer alguns testes mudando as opções de otimização (estava com o default) e instalando uma versão ainda mais recente. O que é estranho é que é uma construção bastante comum. Se o problema se reproduzir com a versão mais recente vou reportar ao fabricante e ver se dão algum retorno (lembrando que esta é uma versão gratuita e sem suporte).
Eu não tenho experiência com o compilador da IAR, uso o ambiente da CrossStudio, que funciona muito bem. A variável "valor" foi declarada como sendo sem sinal (unsigned)? Isso pode fazer a diferença para o compilador.
Adriano,
Esta foi a minha primeira desconfiança. Entretanto, 'valor' está declarado como 'byte', que é typedef para 'unsigned char'. Nos includes do compilador 'P1OPUT' também é definido como 'unsigned char'. Além disso, o código não está testando o bit mais significativo (que seria o sinal).
Postar um comentário