Главная arrow Начинающим arrow Пробуем программировать arrow Вывод чисел на дисплей  
29.03.2017 г.
Главная
Проекты
Статьи
Начинающим
Архив новостей
Ссылки
Контакты
Поиск
Файлы
Форум
Карта сайта
Авторизация





Забыли пароль?
Ещё не зарегистрированы? Регистрация
Поддержи наш сайт!
Через WebMoney

 R785211844650
 Z210696637574
 E368177590409

Простые устройстваОтличные товары по превосходным ценамОтличные товары по превосходным ценам
Вывод чисел на дисплей Печать E-mail
Рейтинг: / 21
ХудшаяЛучшая 
Автор ARV   
14.02.2008 г.

Начинающие самостоятельно писать программы на Си для микроконтроллеров рано или поздно приходят к необходимости вывода информации на какой-то дисплей. Конечно, если используется ЖКИ со встроенным контроллером, а так же готовые библиотеки для символьного ввода-вывода на этот дисплей - проблем нет. Но если используется дисплей на семисегментных индикаторах, или нет возможности использовать функции Си по форматированному выводу (например, из-за ограниченности размера кода) - возникают проблемы с преобразованием числа в его символьное представление.

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

Опишем следующие константы:

 

			#define DIG_BASE  10 /* основание системы считсления для перевода */
			#define MAX_SIZE 6 /* максимальное число выводимых символов */ 
			#define SPACE_CHAR  ' ' /* символ "пустого" места */
			#define NEG_CHAR '-' /* символ "минус" */
			

 

Главное, что вы должны помнить здесь и далее: в тексте программы я применяю символы, удобные для чтения на мониторе компьютера (пробел - это пробел, минус - это минус и т.д.), но в вашей практической программе символы будут совсем иными. Скорее всего это будут битовые представления светящихся сегментов, т.е. для символа "минус" вполне может использоваться значение 0b00010000, которое означает, что светится только один из 8-и сегментов индикатора (8-й сегмент - это точка). 

Теперь опишем глобальные переменные и массивы, которые необходимы нам в работе.

 

			// массив символов, соответствующих ЦИФРАМ выбранной системы счисления
			unsigned char SYMBOLS[DIG_BASE] = {'0','1','2','3','4','5','6','7','8','9'};
			
			unsigned char out[MAX_SIZE]; // выходной массив символов (экранная область)
			

 

Массив SYMBOLS[] - это список символов, которые соответствуют цифрам чисел выбранной системы счисления. Для двоичной достаточно всего двух символов 0 и 1, а для шестнадцатиричной потребуются 16 символов. Массив out[] - это "экранная область" нашего индикатора, т.е. тот массив, содержимое которого непрерывно динамически отображатеся на индикаторе. Разумеется, можно использовать этот массив и для хранения промежуточных преобразований.

 

Теперь займемся собственно преобразованием.

 

			void convert(unsigned int NUM){
			 int i, m;
			 for(i=MAX_SIZE-1; i>=0; i--){
			  // цикл заполнения выходного массива СПРАВА НАЛЕВО
			  m = NUM % DIG_BASE; // находим остаток от деления числа на основание
			  out[i] = SYMBOLS[m]; // этот остаток есть ВЫВОДИМАЯ ЦИФРА
			  NUM /= DIG_BASE;  // уменьшаем число в DIG_BASE раз
			 }
			}
			

 

Функция convert(NUM) заполнит массив out[] символьным представлением беззнакового числа NUM. В своей работе эта функция использует ранее описанные константы, в частности - размер массива out[]. Массив заполняется целиком и справа налево, при этом число, если цифр в нем меньше чем размер массива, выводится с выравниванием вправо и с заполнением свободного места нулями. То есть для нашего примера размер выходного массива равне 6 позициям (т.е. у нас 6 индикаторов), и число 5 будет выведено как 000005, а число 10237 будет выведено как 010237.

Очевидно, что левые нули - лишние, и напрасно загромаждают индикатор. Если необъходимо избавиться от них, модифицируем нашу функцию:

 

			// функция, которая заполняет выходной массив СИМВОЛЬНЫМ представлением числа
			// при этом не выводит НЕЗНАЧАЩИЕ ЛЕВЫЕ нули, т.е. число 1 выводится как '     1'
			void trim_convert(unsigned int NUM){
			 int i, m;
			 for(i=MAX_SIZE-1; i>=0; i--){
			  // цикл заполнения выходного массива СПРАВА НАЛЕВО
			  m = NUM % DIG_BASE; // находим остаток от деления числа на основание
			  if((NUM==0)&&(i!=(MAX_SIZE-1)))
			  // если наше число - ноль и вывод НЕ в правую позицию
			   out[i] = SPACE_CHAR; // то выводим "пустое" место - подавляем незначащие нули
			  else
			   out[i] = SYMBOLS[m]; // иначе выводим символ нужной ЦИФРЫ
			  NUM /= DIG_BASE;  // уменьшаем число в DIG_BASE раз
			 }
			}
			

 

Чтобы левые нули заменялись пустым местом, потребовалось всего-навсего добавить один оператор if. Теперь число 12 будет выглядеть на индикаторе как . . . . 12 (точками я условно обозначил "пустые" индикаторы). По-моему, это гораздо эстетичнее.

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

 

			void s_trim_convert(int NUM){
			 int i, m, sign = 0;
			 if(NUM <0){
			 // если число отрицательное
			  sign = 1; // установим признак наличия знака
			  NUM *= -1; // а само число возьмем по модулю
			 }
			 // выводим уже положительное число
			 i=MAX_SIZE-1;
			 do{
			  // цикл заполнения выходного массива СПРАВА НАЛЕВО
			  m = NUM % DIG_BASE; // находим остаток от деления числа на основание
			  if((NUM==0)&&(i!=(MAX_SIZE-1)))
			   break; // закончим цикл вывода числа
			  else
			   out[i] = SYMBOLS[m]; // иначе выводим символ нужной ЦИФРЫ
			  NUM /= DIG_BASE;  // уменьшаем число в DIG_BASE раз
			 } while (--i >= 0);
			 // число выведено, проверяем свободное место и выводим при необходимости знак
			 if (i < 0) return; // места не хватает - выход
			 if (sign) out[i--] = NEG_CHAR; // выводим знак, если нужно
			 for(; i>=0;i--) out[i] = SPACE_CHAR; // очищаем незначащие позиции 
			}
			

 

Хотя логика работы функции довольно проста, код ее получился более длинным и сложным, чем предыдущие. Сначала мы проверяем, отрицательное ли число надо преобразовать или нет. Отрицательное число берется по модулю и далее начинается вывод, как и раньше. Однако, теперь мы применяем оператор do вместо оператора for (исключительно для примера), и прерываем его работу в момент, когда все число будет преобразовано. В этом месте переменная i будет указывать на место левее числа, т.е. место, где должен быть знак (если он нужен). Если же знак не нужен, то от этого места и до начала массива все ячейки заполняются символом пустого места, избавляя нас от левых нулей и возможного мусора в этих позициях.

 

Благодаря тому, что в наших функциях мы использовали максимально универсальные алгоритмы, они смогут переводить числа в представление любой системы счисления. Например, если значение DIG_BASE сделать равным 8, а массив SYMBOLS "укоротить" до 8 элементов, наши функции будут показывать нам восьмеричные числа, т.е. число 12 отобразится как 14, а 236 как 354. Аналогично и для любых других оснований, только следует помнить, что чем меньше основание системы счисления, тем длиннее символьное представление числа: так, для двоичного представления всего однобайтного числа требуется 8 "знакомест". Еще требуется учесть, что отрицательные числа не всегда существуют во всех системах: в шестнадцатиричном представлении знак минус не применяется никогда, равно как и в двоичном. Вывод больших чисел, чем допускает int (например, long) реализуется простым переопределением типа входной переменной NUM для любой из наших функций.

Часто требуется выводить дробные десятичные числа. Знаю, что многие начинающие сразу же применят тип float и спросят "а как его вывести?" Отвечу, что в 90% случаев применение типа float не требуется, но если все же без него никак, то воспользуйтесь следующим приемом:

 

			#define COMMA_CHAR '.' /* это символ десятичной точки */ 
			// десятичная точка не занимает отдельного места на семисегментном индикаторе !
			
			float tmp = 2.15; // пусть это число, которое надо вывести
			
			convert((int)tmp*100); // выведем увеличенное в 100 раз число tmp, преобразованное к целому
			out[MAX_SIZE-2] |= COMMA_CHAR; // добавим точку к нужному разряду индикатора
			

 

Наша маленькая хитрость заключается в том, что выводим мы, как и ранее, целое число, а десятичную точку просто устанавливаем в такое место. чтобы выглядело все, как положено. Эта хитрость должна объяснить, почему я говорил об отсутствии в 90% необходимости применения типа float: если нам нужна точность расчетов всего 2 знака после запятой, то все наши числа мы заранее умножим на 100 и будем производить вычисления над ними, как целыми числами, а потом, когда вычисления будут завершены, просто "отделим" 2 разряда, превартив их в сотые и десяты доли. Отказ от типа float очень существенно уменьшает размер кода программы, и увеличивает скорость расчетов!

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


Добавить в любимые (1) | Просмотров: 30125

  Коментарии (9)
 1 Написал(а) Сюрикен, в 15:49 21.02.2008
+1 пешы исчо!
 2 Написал(а) Igor, в 19:56 17.03.2008
клёво написал ! только и нифига не понял :sigh
 3 Написал(а) Макс, в 12:24 09.04.2008
Почти все понятно! :) За статейку спасибо!  
Вот только я не догоняю одно: где процедура "загона" символов из массива SYMBOLS[DIG_BASE] с соответствующим знакоместом на дисплее в порт МК подключённый к дисплею ?
 4 Написал(а) ARV, в 17:05 12.04.2008
во-первых, в порт выводятся данные не из массива SYMBOLS, а из массива out - читайте внимательнее. 
во-вторых, процесс вывода на индикатор в статье не рассмотрен (это и не входило в планы) - читайте внимательнее. 
в-третьих, все вопросы задавайте на форуме.
 5 Написал(а) Евгений, в 10:59 03.01.2009
Как это реализовать на ассемблере? Надо вывести 1 байт(тоесть мах 255) на 3х разрядный индикатор. Как в этом байте выделить десятичные разряды?  
Работая в AVR Studio
 6 Написал(а) ARV, в 15:41 08.01.2009
[B]вопросы задавайте на форуме![/B]
 7 Написал(а) Ruslan, в 11:58 25.04.2009
а не скинете схему подключения
 8 Написал(а) Леонид, в 19:20 28.12.2009
Отлично!  
Вопрос: КАК вывести 4 байтное слово (24 битное слово допустим с AD преобразователя) на дисплей в 10 системе счисления?
 9 Написал(а) ARV, в 13:13 31.01.2012
неужели же я написал невнятно?! обидно :( 
чтобы вывести число БОЛЬШЕ int, надо заменить тип переменной-параметра функции convert на long - вот и все дела... 
 
P.S. вопросы и обсуждения прошу задавать на ФОРУМЕ.

Только зарегистрированные пользователи могут оставлять коментарии.
Пожалуйста зарегистрируйтесь или войдите в ваш аккаунт.

 
« Пред.   След. »
Полезные материалы по сходным темам
Кто на сайте?
Сейчас на сайте находятся:
3 гостей
Помощь on-line
BannerFans.com