Вопрос

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

Цель этой функции - принять значение символа без знака от 0 до 99 и вернуть либо его эквивалент в формате ascii в случае, если оно равно 0-9, либо манипулировать небольшим глобальным массивом символов, на который можно ссылаться из вызывающего кода после завершения функции.

Я задаю этот вопрос, потому что два компилятора от одного и того же поставщика интерпретируют этот код по-разному.

Этот код был написан как способ разобрать адресные байты, отправленные через RS485, в строки, которые могут быть легко переданы функции send-lcd-string.

Этот код написан для архитектуры PIC18 (8-разрядный uC).

Проблема в том, что бесплатная / ознакомительная версия определенного компилятора генерирует идеальный ассемблерный код, который работает, хотя и страдает от снижения производительности, но платный и предположительно превосходный компилятор генерирует код более эффективно за счет возможности ссылаться на адреса всех моих байтовых массивов, используемых для управления графикой на моем ЖК-дисплее.

Я знаю, что добавляю много грязи в воду, используя проприетарный компилятор для менее типичной архитектуры, но я надеюсь, что у кого-то есть какие-то предложения.

Спасибо.

Это было полезно?

Решение

деление по модулю и целому числу может быть очень и очень дорогостоящим.Я не знаю о вашей конкретной архитектуре, но, по-моему, там тоже дорого.

Если вам нужно и то, и другое, деление и по модулю, выполните одно из них и получите другое путем умножения / разности.

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

Другие советы

Я бы определенно избегал использования чего-либо с плавающей запятой на рисунке.И я бы - постарался не использовать никаких разделений.Сколько раз вы видели отправку символа, отличного от ascii, на ЖК-дисплей?Можете ли вы сохранить его в памяти ЖК-дисплея, а затем вызвать по его позиции в памяти?

Вот как выглядит деление на 10 в моем коде, обратите внимание на 17 циклов, которые ему необходимо выполнить.Подумайте о том, сколько времени это займет, и убедитесь, что больше ничего не нужно ждать.

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]

Если вы используете что-либо с плавающей запятой в своем коде, загляните в память программы после того, как вы ее скомпилировали, на вкладку Symbolic (чтобы вы действительно могли ее прочитать) и найдите код с плавающей запятой, который необходимо будет включить.Вы найдете его вверху (в зависимости от вашего кода), вскоре после метки _reset .

Мой начинается со строки под номером 223 и адреса памяти 001BC с _ floatsisf, продолжается несколькими дополнительными метками (_fpack, _divsf3 и т.д.) И заканчивается _funpack, последняя строка на 535 и адрес памяти 0042C.Если вы можете обработать (42C-1BC = 0x270 =) 624 байта потерянного программного пространства, отлично, но на некоторых чипах всего 2 кб места, и это не вариант.

Вместо арифметики с плавающей запятой, если это возможно, попробуйте использовать арифметику с фиксированной запятой в базе 2.

Что касается невозможности ссылаться на все массивы байтов на вашем ЖК-дисплее, вы проверили, чтобы убедиться, что вы не пытаетесь отправить null (который является прекрасным адресом), но он останавливается проверкой кода на конец строки ascii?(это случалось со мной и раньше).

Я бы, наверное, написал это как:

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

Является ли плохой формой преобразование в floating, вызов fmod и преобразование в integer вместо простого использования оператора %?Я бы сказал, да.Существуют более понятные способы замедления работы программы для удовлетворения некоторых требований по времени, например, переход в спящий режим в цикле for.Независимо от того, какой компилятор или какая настройка ассемблерного кода или что-то еще, это сильно запутанный способ контролировать скорость выполнения вашей программы, и я называю это плохой формой.

Если идеальный ассемблерный код означает, что он работает правильно, но это даже медленнее, чем преобразования в код с плавающей запятой и обратно, тогда используйте целые числа и переходите в спящий режим в цикле for .

Что касается несовершенного ассемблерного кода, в чем проблема?"за счет возможности ссылаться на адреса всех моих байтовых массивов"?Похоже, что тип char * работает в вашем коде, поэтому кажется, что вы можете обращаться ко всем своим байтовым массивам так, как это предусмотрено стандартом C.В чем проблема?

Честно говоря, я бы сказал, что да..

Если бы ты хотел b чтобы быть остальным, либо используйте MOD, либо используйте свой собственный:

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

Преобразование в / из значений с плавающей точкой никогда не является способом выполнения чего-либо, если только ваши значения действительно не являются значениями с плавающей точкой.

Кроме того, я бы настоятельно рекомендовал придерживаться соглашения о явной ссылке на ваши типы данных как 'signed' или 'unsigned' и оставить голый 'char' для того, когда он на самом деле является символ (часть строки).Вы передаете необработанные данные, которые, по моему мнению, должны быть символом без знака (при условии, конечно, что исходный является без подписи!).Легко забыть, должно ли что-то быть подписано / неподписано, и с голым символом вы получите всевозможные ошибки переноса.

Большинству 8-битных микропроцессоров требуется вечность для умножения (и более чем вечность для деления), поэтому постарайтесь свести их к минимуму.

Надеюсь, это поможет..

Код, похоже, выполняет две совершенно разные вещи, в зависимости от того, задано ли ему число в диапазоне 0-9 или 10-99.По этой причине я бы сказал, что эта функция написана в плохой форме:Я бы разделил вашу функцию на две функции.

Поскольку мы обсуждаем здесь деление на 10..

Это мое мнение.Это только простые операции и даже не требует широких регистров.

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

Приветствия, Нильс

Для оптимизаторов типично время от времени делать ненужные штуковины, если вы копаетесь во внутренних компонентах.

Является ли ваше преобразованное значение глобальным значением или иным образом присвоено таким образом, что компилятор знает, что его не следует трогать?

РИС не любит выполнять арифметику указателей.

Как указывает программист Windows, используйте оператор mod (см. Ниже).)

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

Да, я верю, что ваша функция:
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;

}
}
находится в плохой форме:

Не используйте десятичные числа для символов ASCII, используйте символ, т. е.'@' вместо 0x40.
Нет необходимости в использовании fmode функция.

Вот мой пример:
// Предполагается, что 8-битный октет
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.

Поскольку там всего 3 цифры, я решил размотать петлю.Нет ветвей, прерывающих предварительную выборку инструкций.Никаких исключений с плавающей запятой, только целочисленная арифметика.

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

Просто быть придурком, но множественным return утверждения из одной и той же функции могут считаться дурным тоном (MISRA).

Кроме того, некоторые из приведенных выше обсуждений касаются предела оптимизации перестановок.Некоторые задачи должны быть оставлены компилятору.Однако в такой минималистичной встроенной среде эти приемы все еще могут быть актуальны.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top