Pergunta

char byte_to_ascii(char value_to_convert, volatile char *converted_value) {

 if (value_to_convert < 10) {
  return (value_to_convert + 48);
 } else {
  char a = value_to_convert / 10;
  double x = fmod((double)value_to_convert, 10.0);
  char b = (char)x;
  a = a + 48;
  b = b + 48;
  *converted_value = a;
  *(converted_value+1) = b;
  return 0;
 }
}

O objectivo desta função é a de ter um valor unsigned char de 0 a 99 e retornar Ou é ascii equivalente no caso de ser 0-9 ou manipular uma pequena matriz de caracteres global que pode ser referenciada a partir da função seguinte código de chamada conclusão.

Faço esta pergunta porque dois compiladores do mesmo fornecedor interpretar este código de maneiras diferentes.

Este código foi escrito como uma forma de endereço parse bytes enviados via RS485 em strings que podem ser facilmente passados ??para uma função send-lcd-string.

Este código é escrito para a arquitetura PIC18 (8 bits uC).

O problema é que a versão gratuita / avaliação de um compilador particular, gera código de montagem perfeita que as obras, enquanto que sofrem um impacto no desempenho, mas o compilador pago e supostamente superior gera um código mais eficiente à custa de ser referência capazes os endereços de todos meus matrizes de bytes usado para conduzir os gráficos na tela da minha lcd.

Eu sei que eu estou colocando muita lama na água usando um compilador proprietário para um menos de arquitectura típica, mas espero que alguém lá fora, tem algumas sugestões.

Graças.

Foi útil?

Solução

modulo e divisão inteira pode ser muito, muito caro. Eu tenho não sei sobre a sua arquitetura particular, mas meu palpite lá é caro também.

Se você precisa de ambos, divisão e módulo, fazer um deles e obter o outro pela multiplicação / diferença.

q =p/10;
r = p - q*10;

Outras dicas

Eu definitivamente evitar o uso de ponto flutuante qualquer coisa em um PIC. E eu gostaria de -experimentar não to- usar quaisquer divisões. Quantas vezes você vê o envio de um caractere não-ascii ao LCD? pode guardá-lo para a memória do LCD e, em seguida, chamá-lo por sua posição de memória?

Aqui está o que uma divisão por 10 looks como no meu código, observe os 17 ciclos de que necessita para ser concluído. Pense em quanto tempo isso vai demorar, e certifique-se não há mais nada de espera sobre isso.

61:                         q = d2 / 10;
 01520  90482E     mov.b [0x001c+10],0x0000
 01522  FB8000     ze 0x0000,0x0000
 01524  2000A2     mov.w #0xa,0x0004
 01526  090011     repeat #17
 01528  D88002     div.uw 0x0000,0x0004
 0152A  984F00     mov.b 0x0000,[0x001c+8]

Se você fizer uma coisa em ponto flutuante no seu código, procure na memória do programa depois que você compilou, na guia simbólico (para que você possa realmente lê-lo) e olhar para o código de ponto flutuante que terá de ser incluído. Você vai encontrá-lo perto do topo (dependendo do seu código), em breve (ish) após o rótulo _reset.

Mina começa no número 223 e memória linha de endereço do 001BC com _ floatsisf, continua através de vários rótulos adicionais (_fpack, _divsf3, etc) e termina em _funpack, última linha a 535 e 0042C endereço de memória. Se você pode lidar com (42C-1BC = 0x270 =) 624 bytes de espaço programa perdido, grande, mas alguns chips têm apenas 2k de espaço e isso não é uma opção.

Em vez de ponto flutuante, se for possível, tente usar aritmética de ponto fixo, na base 2.

Tanto quanto não ser capaz de referenciar todas as matrizes de bytes em seu LCD, você tem verificado para se certificar de que você não está tentando enviar um nulo (que é o endereço de um bem) mas obter do parado por verificação de código para o fim de uma seqüência de caracteres ASCII? (Isso aconteceu comigo antes).

eu provavelmente escrever que, como:

char byte_to_ascii(char value_to_convert, volatile char *converted_value)
{
 if (value_to_convert < 10) {
  return value_to_convert + '0';
 } else {
  converted_value[0] = (value_to_convert / 10) + '0';
  converted_value[1] = (value_to_convert % 10) + '0';
  return 0;
 }
}

É má forma para converter em flutuante, fmod chamada, e convertido para inteiro, em vez de apenas usando o operador%? Eu diria que sim. Há maneiras mais legíveis para abrandar um programa para atender a algumas exigência de tempo, por exemplo dormir em um loop. Não importa o compilador ou o ajuste de código de montagem ou qualquer outra coisa, esta é uma maneira altamente ofuscado para controlar a velocidade de execução do seu programa, e eu chamá-lo de forma pobre.

Se os meios de código de montagem perfeitas que ele funciona bem, mas é ainda mais lento do que as conversões de ponto flutuante e para trás, em seguida, usar inteiros e dormir em um loop.

Quanto ao código de montagem imperfeita, qual é o problema? "À custa de ser referência capazes os endereços de todos os meus matrizes de bytes"? Parece que tipo char * está trabalhando em seu código, por isso parece que você pode resolver todas as suas matrizes de bytes a forma como o padrão C diz que você pode. Qual é o problema?

Francamente, eu diria que sim ..

Se você quisesse b para ser o restante, use MOD ou roll-seu-próprio:

char a = value_to_convert / 10;
char b = value_to_convert - (10 * a);

Conversão de / para flutuadores nunca é a maneira de fazer as coisas, a menos que seus valores são realmente carros alegóricos.

Além disso, eu recomendo fortemente a vara para a convenção de se referir explicitamente a seus tipos de dados como 'assinado' ou 'sem sinal', e deixar o 'char' nua para quando ele realmente é um personagem (parte de uma string). Você está passando em dados brutos, o que me parece que deve ser um unsigned char (assumindo, claro, que a fonte é sem sinal!). É fácil esquecer-se se algo deve ser assinado / sem assinatura e com um char nua, você vai ter todos os tipos de erros roll-over.

A maioria dos micros de 8 bits demorar uma eternidade para uma multiplicação (e mais do que sempre para uma divisão), e assim tentar minimizar estes.

Espero que isso ajude ..

O código parece estar fazendo duas coisas muito diferentes, dependendo se ele é dado um número no intervalo 0-9 ou 10-99. Por essa razão, eu diria que esta função é escrita em forma de pobres: Eu iria dividir a função em duas funções

.

Uma vez que estamos discutindo divisões por 10 aqui ..

Esta é a minha opinião. É apenas operações simples e nem sequer precisa de registros de largura.

unsigned char divide_by_10 (unsigned char value)
{
  unsigned char q;
  q = (value>>1) + (value>>2);
  q += (q>>4);
  q >>= 3;
  value -= (q<<3)+q+q;
  return q+((value+6)>>4);
}

Cheers, Nils

É típico para otimizadores fazer coisinhas indesejados ao longo do tempo se você fuçar nas partes internas.

é seu converted_value um valor global ou de outra forma atribuído de forma tão a que o compilador não sabe tocá-lo?

de

PIC não gosta de fazer aritmética de ponteiro.

Como o Windows pontos fora programador, utilize o operador mod (veja abaixo).

char byte_to_ascii(char value_to_convert, volatile char *converted_value) {

 if (value_to_convert < 10) {
  return (value_to_convert + 48);
 } else {
  char a = value_to_convert / 10;  
  char b = value_TO_convert%10;
  a = a + 48;
  b = b + 48;
  *converted_value = a;
  *(converted_value+1) = b;
  return 0;
 }
}

Sim, eu acredito que a sua função:
char byte_to_ascii(char value_to_convert, volatile char *converted_value) {

if (value_to_convert < 10) {
    return (value_to_convert + 48);
} else {

char a = value_to_convert / 10;
double x = fmod((double)value_to_convert, 10.0);
char b = (char)x;
a = a + 48;
b = b + 48;
*converted_value = a;
*(converted_value+1) = b;
return 0;

}
}
is in poor form:

Don't use decimal numbers for ASCII chars, use the character, i.e. '@' instead of 0x40.
There is no need for using the fmode function.

Here is my example:
// Assuming 8-bit octet
char value;
char largeValue;
value = value_to_convert / 100;
value += '0';
converted_value[0] = value;
largeValue = value_to_convert - value * 100;
value = largeValue / 10; value += '0';
converted_value[1] = value;
largeValue = largeValue - value * 10; value += '0';
converted_value[2] = value;
converted_value[3] = '\0'; // Null terminator.

Since there are only 3 digits, I decided to unroll the loop. There are no branches to interrupt the prefetching of instructions. No floating point exceptions, just integer arithmetic.

If you leading spaces instead of zeros, you can try this:
value = (value == 0) ? ' ' : value + '0';

Apenas para ser um nitwitt, mas várias instruções return da mesma função pode ser considerado má forma (MISRA).

Além disso, algumas das discussões acima estão no limite de otimizações permature. Algumas tarefas deve ser deixada para o compilador. No entanto, em um ambiente incorporado, minimalista, estes truques pode ser válido ainda.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top