Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Светодиодиодна индикация на PIC18F6722
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > MCS51, AVR, PIC, STM8, 8bit
Andbiz
Здравствуйте, уважаемые форумчане!
Пробую освоить микроконтроллер PIC18F6722. Программа для работы – MPLAB IDE v8.86 + компилятор С18. Программатор – MPLAB ICD3. Есть плата, на которой уже установлен данный микроконтроллер и есть светодиоды, которые можно «позажигать», чтобы немного разобраться в работе данного МК.

Вот схемы платы:
Нажмите для просмотра прикрепленного файла
Нажмите для просмотра прикрепленного файла
Нажмите для просмотра прикрепленного файла

Микроконтроллер показан на третьей схеме.
Кварц стоит внешний – на 6 Мгц. Также используется внешний сторожевой ждущий таймер ADM706, который подключен к выводу 2 (RE0), которым нужно периодически давать импульсы, чтобы сбрасывать данный таймер и он не подал сигнал на перезапуск МК (я так понимаю).

На плате есть 8 семисегментных индикаторов HL1-HL8 (KingsBright SA56-11EWA).
Я решил написать программу, при которой на них будет написаны числа 12345678.
Сигналы на выбора сегмента (D0-D7) подключены от МК к индикатору через буферный регистр хранения информации ЭКФ1533ИР22. Сигналы на разрешение включения индикатора LE4 – LE11 подаются на индикатор с того же буферного регистра ЭКФ1533ИР22, на который данные сигналы подаются с МК через дешифратор ЭКФ1533ИД7 и инвертор сигналов ЭКФ1533ЛН1. С данными микросхемами я разобрался и написал для себя таблицу значений выходов МК, при которой будет зажигаться необходимая мне комбинация цифр.

Текст программы написал следующий:

Код
#include <p18f6722.h>

#pragma config OSC = XT // Применяю внешний кварцевый резонатор
#pragma config WDT = OFF // Внутренний сторожевой таймер отключаю - применяется внешний, который нужно периодически обнулять

void main (void)
{
    TRISD = 0;
    TRISE = 0;

    PORTD = 0; // Сбрасываю значения на порте D
    PORTE = 0; // Сбрасываю значения на порте E
    
    PORTD = 0xF9; // Принимаю соответствующие значения сегментов для индикатора HL1
    PORTE = 0x3; // Разрешаю индикацию индикатора HL1

    PORTD = 0xA4; // Принимаю соответствующие значения сегментов для индикатора HL2
    PORTE = 0x4; // Разрешаю индикацию индикатора HL2

    PORTD = 0xB0; // Принимаю соответствующие значения сегментов для индикатора HL3
    PORTE = 0x5; // Разрешаю индикацию индикатора HL3

    PORTD = 0x99; // Принимаю соответствующие значения сегментов для индикатора HL4
    PORTE = 0x6; // Разрешаю индикацию индикатора HL4

    PORTD = 0x92; // Принимаю соответствующие значения сегментов для индикатора HL5
    PORTE = 0x7; // Разрешаю индикацию индикатора HL5

    PORTD = 0x82; // Принимаю соответствующие значения сегментов для индикатора HL6
    PORTE = 0x8; // Разрешаю индикацию индикатора HL6

    PORTD = 0xF8; // Принимаю соответствующие значения сегментов для индикатора HL7
    PORTE = 0x9; // Разрешаю индикацию индикатора HL7

    PORTD = 0x80; // Принимаю соответствующие значения сегментов для индикатора HL8
    PORTE = 0xA; // Разрешаю индикацию индикатора HL8
    
    while (1); // Организую бесконечный цикл, для того, чтобы индикация светодиодов была постоянной
}


Программу скомпилировал, все прошло нормально, но прошивать МК еще не стал – решил спросить совета. Как я понимаю, при данной программе на мгновение должна появиться комбинация 12345678, т.к. с вывод микроконтроллера RE0 сигнал подается на ждущий таймер и индикаторы. Данный вывод у меня будет задействован в индикации и будет периодически менять свое значение, т.е. будет сбрасываться. Правильно ли я думаю? Правильно ли я написал программу? Правильно ли я указал конфигурационные биты в начале программы?
Andbiz
Программу записал в МК.
В результате получилась следующая ситуация на индикаторах:
Индикатор HL1 – 2
Индикатор HL2 – 3
Индикатор HL3 – 4
Индикатор HL4 – 5
Индикатор HL5 – 6
Индикатор HL6 – 7
Индикатор HL7 – 8
Индикатор HL8 – 8

Нажмите для просмотра прикрепленного файла

Получилась ситуация, что все разряды сдвинулись на одно значение в сторону. Схема верная – прозвонил, ошибок в ней нет.
Дешифратор работает верно.
Сигналы LE – это выходы с инверторов сигналов (D5-D6).

Должно быть:
LE1 – не задействован в программе
LE2 – не задействован в программе
LE3 – не задействован в программе
LE4 – разрешение включения индикатора HL1
LE5 – разрешение включения индикатора HL2
LE6 – разрешение включения индикатора HL3
LE7 – разрешение включения индикатора HL4
LE8 – разрешение включения индикатора HL5
LE9 – разрешение включения индикатора HL6
LE10 – разрешение включения индикатора HL7
LE11 – разрешение включения индикатора HL8
LE12 – отсутствует, на схеме не подключен

На деле:
LE1 – неизвестно
LE2 – неизвестно
LE3 – неизвестно
LE4 – неизвестно
LE5 – разрешение включения индикатора HL1
LE6 – разрешение включения индикатора HL2
LE7 – разрешение включения индикатора HL3
LE8 – разрешение включения индикатора HL4
LE9 – разрешение включения индикатора HL5
LE10 – разрешение включения индикатора HL6
LE11 – разрешение включения индикатора HL7
LE12 – разрешение включения индикатора HL8

Т.е. получилась ситуация, что все сигналы сдвинулись на одно значение. При помощи сигнала LE12 я могу зажигать индикатор HL8 и записывать в него любые значения. При этом этот вывод неподключен. Дешифратор работает верно, значения все записываю верно.
Уже потратил весь день на поиски причины этого сдвига. Из-за чего это может происходить?
Ruslan1
на ИР22 (SN74LS373) остаются (хранятся) те данные, которые были во время перехода H->L.
то есть те данные, которые были в момент откючения канала дешифратора (Дешифратор активный 0, но у Вас еще инвертор после него есть)
Делайте выводы.

Но с кодом программы нужно что-то делать.
1. Коментарии должны быть полезны, а не просто набор слов.
2. Не применяйте константы в коде, пользуйтесь #define


Пишите как-то так
Код
#define ADDR_HL1 0x03
#define ADDR_HL2 0x04
#define ADDR_HL3 0x05
#define ADDR_HL4 0x06
#define ADDR_HL5 0x07
#define ADDR_HL6 0x08
#define ADDR_HL7 0x09
#define ADDR_HL8 0x0A
#define ADDR_HL_NO_SELECT 0x00    //все селекты HL1-HL8 неактивны

#define LEDCODE_0    0xF9
#define LEDCODE_1    0x19
#define LEDCODE_2    0x29
#define LEDCODE_3    0x39
#define LEDCODE_4    0x49
#define LEDCODE_5    0x59
#define LEDCODE_6    0x69
#define LEDCODE_7    0x79
#define LEDCODE_8    0x89
#define LEDCODE_9    0x99


#define PORT_DATA PORTD
#define PORT_ADDR PORTE

// определение функций
void Indicator(char addrCode, char dataCode);


//сами функции
void Indicator(char addrCode, char dataCode)
{
    PORT_ADDR = addrCode;
    PORT_DATA = dataCode;
    PORT_ADDR = ADDR_HL_NO_SELECT;     // в этот момент защелкиваются данные на HL1
}



void main (void)
{
    TRISD = 0;    //порты на вывод
    TRISE = 0;

    Indicator(ADDR_HL1,LEDCODE_1);
    Indicator(ADDR_HL2,LEDCODE_2);
    Indicator(ADDR_HL3,LEDCODE_3);
    Indicator(ADDR_HL4,LEDCODE_4);
    Indicator(ADDR_HL5,LEDCODE_5);
    Indicator(ADDR_HL6,LEDCODE_6);
    Indicator(ADDR_HL7,LEDCODE_7);
    Indicator(ADDR_HL8,LEDCODE_8);
      
        while(1);  // ждем выключения или ресета
}


И еще, по схеме:
1. Скажите большое спасибо тому, кто нарисовал на схеме инвертор ЛН1 без обозначения инверсии выходов. Это инвертор. там кружочки нарисовать хорошо бы. Как, впрочем и у ИД7.
2. Второе спасибо за то, что "разработчик" платы разработал также и нестандартный порядок пинов на ICSP разъеме внутрисхемного программирования. Стандартный порядок: Vpp, Vdd,Vss, PGD, PGC.

Основной штамп на первом листе Вы благоразумно затерли, иначе я бы оперировал конкретным именем разработчика sm.gif
ILYAUL
Цитата
1. Коментарии должны быть полезны, а не просто набор слов.
+ слово ПОРТ в данной фразе не склоняется
Цитата
Сбрасываю значения на порте D

Сбрасываю значения на ПОРТ D
Andbiz
Большое спасибо за помощь!

Цитата
на ИР22 (SN74LS373) остаются (хранятся) те данные, которые были во время перехода H->L.
то есть те данные, которые были в момент отключения канала дешифратора (Дешифратор активный 0, но у Вас еще инвертор после него есть)


Я обратил внимание на это. Но потом подумал, что это будет влиять в тот, момент, когда индикация будет меняться. А у меня она пока не изменяется.

По поводу комментариев – поправил.
По поводу констант понял.
По поводу склонения слова «Порт» - буду иметь ввиду.

Цитата
Основной штамп на первом листе Вы благоразумно затерли, иначе я бы оперировал конкретным именем разработчика


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

Текст программы после исправления получился вот таким:

Код
#include <p18f6722.h>

#pragma config OSC = XT        // Применяю внешний кварцевый резонатор
#pragma config WDT = OFF    // Внутренний сторожевой таймер отключаю - применяется внешний, который нужно периодически обнулять сигналом ADR0

#define ADDR_HL1 0x3    // определяю адрес первого индикатора HL1 0x03, как переменную ADDR_HL1
#define ADDR_HL2 0x4    // определяю адрес второго индикатора HL2 0x04, как переменную ADDR_HL2
#define ADDR_HL3 0x5    // определяю адрес третьего индикатора HL3 0x05, как переменную ADDR_HL3
#define ADDR_HL4 0x6    // определяю адрес четвертого индикатора HL4 0x06, как переменную ADDR_HL4
#define ADDR_HL5 0x7    // определяю адрес пятого индикатора HL5 0x07, как переменную ADDR_HL5
#define ADDR_HL6 0x8    // определяю адрес шестого индикатора HL6 0x08, как переменную ADDR_HL6
#define ADDR_HL7 0x9    // определяю адрес седьмого индикатора HL7 0x09, как переменную ADDR_HL7
#define ADDR_HL8 0xA    // определяю адрес восьмого индикатора HL8 0x0A, как переменную ADDR_HL8
#define ADDR_HL_NO_SELECT 0x00       //определяю переменную, при которой не будет не задействован ни один индикатор HL1-HL8

#define LEDCODE_0 0xC0    //определяю переменную, при которой на индикаторе будет отображаться "0"
#define LEDCODE_1 0xF9    //определяю переменную, при которой на индикаторе будет отображаться "1"
#define LEDCODE_2 0xA4    //определяю переменную, при которой на индикаторе будет отображаться "2"
#define LEDCODE_3 0xB0    //определяю переменную, при которой на индикаторе будет отображаться "3"
#define LEDCODE_4 0x99    //определяю переменную, при которой на индикаторе будет отображаться "4"
#define LEDCODE_5 0x92    //определяю переменную, при которой на индикаторе будет отображаться "5"
#define LEDCODE_6 0x82    //определяю переменную, при которой на индикаторе будет отображаться "6"
#define LEDCODE_7 0xF8    //определяю переменную, при которой на индикаторе будет отображаться "7"
#define LEDCODE_8 0x80    //определяю переменную, при которой на индикаторе будет отображаться "8"
#define LEDCODE_9 0x90    //определяю переменную, при которой на индикаторе будет отображаться "9"

#define PORT_DATA PORTD    //определяю PORTD, как переменную PORT_DATA
#define PORT_ADDR PORTE    //определяю PORTE, как переменную PORT_ADDR

void Indicator(char addrCode, char dataCode);    //определяю функцию с именем Indicator, char - символьный тип данных

void Indicator(char addrCode, char dataCode)    //ввожу параметры функции, т.е. значения, которые будут находиться под именам addrCode и dataCode.
{
    PORT_ADDR = addrCode;    //под параметром addrCode будет значение переменной PORT_ADDR
    PORT_DATA = dataCode;    //под параметром dataCode будет значение переменной PORT_DATA
}

void main (void)    // точка входа в саму программу
{
    TRISD = 0;    // выводы порта D микроконтроллера определяю, как выходы
    TRISE = 0;    // выводы порта Е микроконтроллера определяю, как выходы

    Indicator(ADDR_HL1,LEDCODE_1);    //на индикатор HL1 вывожу число "1"
    Indicator(ADDR_HL2,LEDCODE_2);    //на индикатор HL2 вывожу число "2"
    Indicator(ADDR_HL3,LEDCODE_3);    //на индикатор HL3 вывожу число "3"
    Indicator(ADDR_HL4,LEDCODE_4);    //на индикатор HL4 вывожу число "4"
    Indicator(ADDR_HL5,LEDCODE_5);    //на индикатор HL5 вывожу число "5"
    Indicator(ADDR_HL6,LEDCODE_6);    //на индикатор HL6 вывожу число "6"
    Indicator(ADDR_HL7,LEDCODE_7);    //на индикатор HL7 вывожу число "7"
    Indicator(ADDR_HL8,LEDCODE_8);    //на индикатор HL8 вывожу число "8"
      
        while(1);  // Организую бесконечный цикл, для того, чтобы индикация светодиодов была постоянной. Цикл прерывается перезапуском или выключением платы.
}


Переменным ADDR, LEDCODE, PORT_DATA и PORT_ADDR приравнены константы вне функции main, поэтому они глобальные.

void Indicator(char addrCode, char dataCode); - тут, как я понимаю, производиться объявление функции до ее использования. Void – ставиться, т.к. в моем понимании значение функции «возвращается», используется в дальнейшем участке схемы, т.е. сюда «возвращаются».

Код
//сами функции
void Indicator(char addrCode, char dataCode)
{
    PORT_ADDR = addrCode;
    PORT_DATA = dataCode;
    
}


Тут, как я понимаю, производиться заполнение фунции, объявленной ранее, т.е. приравниваются значения PORT_ADDR и PORT_DATA некоторым значениям, которые будут указаны позже в тексте программы. Void – ставиться, т.к. сюда также возвращаются в дальнейшем при работе самой программы.
Текст самой программы обозначается void main (void) – когда работа доходит до этого участка, то все работает на этом участке и по мере необходимости (в процессе выполнения программы) происходит возврат в участки, обозначенные, как . Void, в которых происходит «переопределение» каких-либо переменных.

Вопросы:
1) Ваша программа полностью работает, если убрать строчку:
Код
    PORT_ADDR = ADDR_HL_NO_SELECT;     // в этот момент защелкиваются данные на HL1

Если она есть, код 12345678 на индикаторах высвечивается, но периодически появляются одно временно восьмерки на HL3 и HL7 или HL5 и HL7.
Без этой строчки все хорошо. А зачем она?
2) В моей программе не использовалось предопределение констант перемеными при помощи #define и не использовалась функция. В остальном она как будто-бы подобна Вашей. Но Ваша все же работает. Почему не работала моя программа? Почему происходила неправильная индикация?
Ruslan1
Цитата(Andbiz @ Aug 20 2012, 18:28) *
Я обратил внимание на это. Но потом подумал, что это будет влиять в тот, момент, когда индикация будет меняться. А у меня она пока не изменяется.

Принцип такой:
1) LE на ИР22 неактивный (L)- значение на выходах не зависит от значения на входе.
2) LE на ИР22 активный (H) - значение на выходах то же самое что значение на входах
3) переход LE H->L: дальше на выходах то значение, которое было в момент этого перехода.

То есть нужно сначала выставить данные и установить адрес для активизации ИР22, после этого данные появятся на выходе ИР22 (на индикаторе). Дальше нужно отключить активность ИР22, запомнятся (будут продолжать высвечиваться именно те данные, которые были в момент "деактивации" LE данной ИР22.

Цитата(Andbiz @ Aug 20 2012, 18:28) *
Переменным ADDR, LEDCODE, PORT_DATA и PORT_ADDR приравнены константы вне функции main, поэтому они глобальные.

void Indicator(char addrCode, char dataCode); - тут, как я понимаю, производиться объявление функции до ее использования. Void – ставиться, т.к. в моем понимании значение функции «возвращается», используется в дальнейшем участке схемы, т.е. сюда «возвращаются».

Тут, как я понимаю, производиться заполнение фунции, объявленной ранее, т.е. приравниваются значения PORT_ADDR и PORT_DATA некоторым значениям, которые будут указаны позже в тексте программы. Void – ставиться, т.к. сюда также возвращаются в дальнейшем при работе самой программы.
Текст самой программы обозначается void main (void) – когда работа доходит до этого участка, то все работает на этом участке и по мере необходимости (в процессе выполнения программы) происходит возврат в участки, обозначенные, как . Void, в которых происходит «переопределение» каких-либо переменных.

Имеет смысл перечитать что-нибудь по языку Си, базовый уровень. У Вас некоторое смешение понятий. Некоторые фразы просто непонятны, может быть Вы и правильно понимаете смысл происходящего, но оперируете доморощенной терминологией.

Цитата(Andbiz @ Aug 20 2012, 18:28) *
Код
    PORT_ADDR = ADDR_HL_NO_SELECT;     // в этот момент защелкиваются данные на HL1

Если она есть, код 12345678 на индикаторах высвечивается, но периодически появляются одно временно восьмерки на HL3 и HL7 или HL5 и HL7.
Без этой строчки все хорошо. А зачем она?
2) В моей программе не использовалось предопределение констант перемеными при помощи #define и не использовалась функция. В остальном она как будто-бы подобна Вашей. Но Ваша все же работает. Почему не работала моя программа? Почему происходила неправильная индикация?

Чудес не бывает, нужно понять почему и что. Разверните фразу "периодически появляются одно временно восьмерки на HL3 и HL7 или HL5 и HL7." Непонятно, что за "периодически" если программа зациклена в конце.

попробуйте
Код
#define ADDR_HL_NO_SELECT 0x0F

Адрес 0 (LE1) в схеме присутствует, и куда идут выходы соответствующего регистра- неясно, может в этом дело.

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

И вообще. У Вас же в руках ICD3, это отладчик. sm.gif
Пройдитесь пошагово по Вашей программе, посмотрите в какой момент после какой команды что происходит, что зажигается-что сбивается в индикации, сразу многое станет ясно.
Andbiz
Сигнал LE1 идет на микросхему D7 ЭКФ1533ИР22 (первая схема)
Далее сигналы с этой микросхему идут на ключи для управления внешними светодиодами:
Нажмите для просмотра прикрепленного файла

Цитата
Чудес не бывает, нужно понять почему и что. Разверните фразу "периодически появляются одно временно восьмерки на HL3 и HL7 или HL5 и HL7." Непонятно, что за "периодически" если программа зациклена в конце.


После записи программы в МК отображается необходимая комбинация 12345678. Затем через 1-2 секунды на 0,2-0,5 секунды зажигаются одновременно все сегменты HL3 и HL7 или HL5 и HL7. Зажигаются парно и быстро тухнут. Если убрать строчку PORT_ADDR = ADDR_HL_NO_SELECT; из программы, то цифры отображаются нормально.

Цитата
Принцип такой:
1) LE на ИР22 неактивный (L)- значение на выходах не зависит от значения на входе.
2) LE на ИР22 активный (H) - значение на выходах то же самое что значение на входах
3) переход LE H->L: дальше на выходах то значение, которое было в момент этого перехода.

То есть нужно сначала выставить данные и установить адрес для активизации ИР22, после этого данные появятся на выходе ИР22 (на индикаторе). Дальше нужно отключить активность ИР22, запомнятся (будут продолжать высвечиваться именно те данные, которые были в момент "деактивации" LE данной ИР22.


Я это понимаю. Хочу попытаться попробовать попытаться осуществить счет на индикаторах от нолях до произвольного значения. Здесь понадобиться обнулять значения.
Andbiz
Написал другую программу. Предприятие, на котором я работаю, связано с обслуживание электроприводов. При этом на приводах часто применяют индикацию, при которой семисегментные индикаторы зажигают свои сегменты в соответствии с вращением двигателя – т.е. по часовой, либо против часовой. Частота изменения сегментов соответствует скорости вращения двигателя.
Предложили попробовать написать такую программу, чтобы она просто изменяла свои сегменты по часовой стрелке. Попробовал – получилась вот такая:

Код
#include <p18f6722.h>

#pragma config OSC = XT        // Применяю внешний кварцевый резонатор
#pragma config WDT = OFF    // Внутренний сторожевой таймер отключаю - применяется внешний, который нужно периодически обнулять сигналом ADR0

#define ADDR_HL1 0x3    // определяю адрес первого индикатора HL1 0x03, как переменную ADDR_HL1
#define ADDR_HL2 0x4    // определяю адрес второго индикатора HL2 0x04, как переменную ADDR_HL2
#define ADDR_HL3 0x5    // определяю адрес третьего индикатора HL3 0x05, как переменную ADDR_HL3
#define ADDR_HL4 0x6    // определяю адрес четвертого индикатора HL4 0x06, как переменную ADDR_HL4
#define ADDR_HL5 0x7    // определяю адрес пятого индикатора HL5 0x07, как переменную ADDR_HL5
#define ADDR_HL6 0x8    // определяю адрес шестого индикатора HL6 0x08, как переменную ADDR_HL6
#define ADDR_HL7 0x9    // определяю адрес седьмого индикатора HL7 0x09, как переменную ADDR_HL7
#define ADDR_HL8 0xA    // определяю адрес восьмого индикатора HL8 0x0A, как переменную ADDR_HL8
#define ADDR_HL_NO_SELECT 0x00       //определяю переменную, при которой не будет не задействован ни один индикатор HL1-HL8

#define LEDCODE_X 0xFF    //определяю переменную, при которой на индикаторе не будет отображаться ни один сегмент
#define LEDCODE_a 0xFE    //определяю переменную, при которой на индикаторе будет отображаться сегмент "a"
#define LEDCODE_b 0xFD    //определяю переменную, при которой на индикаторе будет отображаться сегмент "b"
#define LEDCODE_c 0xFB    //определяю переменную, при которой на индикаторе будет отображаться сегмент "c"
#define LEDCODE_d 0xF7    //определяю переменную, при которой на индикаторе будет отображаться сегмент "d"
#define LEDCODE_e 0xEF    //определяю переменную, при которой на индикаторе будет отображаться сегмент "e"
#define LEDCODE_f 0xDF    //определяю переменную, при которой на индикаторе будет отображаться сегмент "f"
#define LEDCODE_g 0xBF    //определяю переменную, при которой на индикаторе будет отображаться сегмент "g"

#define PORT_DATA PORTD    //определяю PORTD, как переменную PORT_DATA
#define PORT_ADDR PORTE    //определяю PORTE, как переменную PORT_ADDR

void Indicator(char addrCode, char dataCode);    //определяю функцию с именем Indicator, char - символьный тип данных

void Indicator(char addrCode, char dataCode)    //ввожу параметры функции, т.е. значения, которые будут находиться под именам addrCode и dataCode.
{
    PORT_ADDR = addrCode;    //под параметром addrCode будет значение переменной PORT_ADDR
    PORT_DATA = dataCode;    //под параметром dataCode будет значение переменной PORT_DATA
}

    void delay (void)        //организую подпрограмму задержки
    {
        unsigned int i;
        for (i==0; i<6000; i++);    //длительность задержки опрделяется величиной i
    }

void main (void)    // точка входа в саму программу
{
    TRISD = 0;    // выводы порта D микроконтроллера определяю, как выходы
    TRISE = 0;    // выводы порта Е микроконтроллера определяю, как выходы
    
    Indicator(ADDR_HL1,LEDCODE_X);    //сбрасываю индикатор HL1
    Indicator(ADDR_HL2,LEDCODE_X);    //сбрасываю индикатор HL2
    Indicator(ADDR_HL3,LEDCODE_X);    //сбрасываю индикатор HL3
    Indicator(ADDR_HL4,LEDCODE_X);    //сбрасываю индикатор HL4
    Indicator(ADDR_HL5,LEDCODE_X);    //сбрасываю индикатор HL5
    Indicator(ADDR_HL6,LEDCODE_X);    //сбрасываю индикатор HL6
    Indicator(ADDR_HL7,LEDCODE_X);    //сбрасываю индикатор HL7
    Indicator(ADDR_HL8,LEDCODE_X);    //сбрасываю индикатор HL8

           while(1)  // Организую бесконечный цикл
           {
            char j, n; // объявляю локальную переменную n - число счета и j - счетчик
            n=6;
            for (j=0;j<n;j++)    //начинаю цикл перебора сегментов
                {
                    if (j==0)
                    {
                        Indicator(ADDR_HL1,LEDCODE_a);    //зажигаю сегмент "a"
                        Indicator(ADDR_HL2,LEDCODE_a);    //зажигаю сегмент "a"
                        Indicator(ADDR_HL3,LEDCODE_a);    //зажигаю сегмент "a"
                        Indicator(ADDR_HL4,LEDCODE_a);    //зажигаю сегмент "a"
                        Indicator(ADDR_HL5,LEDCODE_a);    //зажигаю сегмент "a"
                        Indicator(ADDR_HL6,LEDCODE_a);    //зажигаю сегмент "a"
                        Indicator(ADDR_HL7,LEDCODE_a);    //зажигаю сегмент "a"
                        Indicator(ADDR_HL8,LEDCODE_a);    //зажигаю сегмент "a"
                        delay ();    //пауза
                    }
                    else if (j==1)
                    {
                        Indicator(ADDR_HL1,LEDCODE_b);    //зажигаю сегмент "b"
                        Indicator(ADDR_HL2,LEDCODE_b);    //зажигаю сегмент "b"
                        Indicator(ADDR_HL3,LEDCODE_b);    //зажигаю сегмент "b"
                        Indicator(ADDR_HL4,LEDCODE_b);    //зажигаю сегмент "b"
                        Indicator(ADDR_HL5,LEDCODE_b);    //зажигаю сегмент "b"
                        Indicator(ADDR_HL6,LEDCODE_b);    //зажигаю сегмент "b"
                        Indicator(ADDR_HL7,LEDCODE_b);    //зажигаю сегмент "b"
                        Indicator(ADDR_HL8,LEDCODE_b);    //зажигаю сегмент "b"
                        delay ();    //пауза
                    }
                    else if (j==2)
                    {
                        Indicator(ADDR_HL1,LEDCODE_c);    //зажигаю сегмент "c"
                        Indicator(ADDR_HL2,LEDCODE_c);    //зажигаю сегмент "c"
                        Indicator(ADDR_HL3,LEDCODE_c);    //зажигаю сегмент "c"
                        Indicator(ADDR_HL4,LEDCODE_c);    //зажигаю сегмент "c"
                        Indicator(ADDR_HL5,LEDCODE_c);    //зажигаю сегмент "c"
                        Indicator(ADDR_HL6,LEDCODE_c);    //зажигаю сегмент "c"
                        Indicator(ADDR_HL7,LEDCODE_c);    //зажигаю сегмент "c"
                        Indicator(ADDR_HL8,LEDCODE_c);    //зажигаю сегмент "c"
                        delay ();    //пауза
                    }
                    else if (j==3)
                    {
                        Indicator(ADDR_HL1,LEDCODE_d);    //зажигаю сегмент "d"
                        Indicator(ADDR_HL2,LEDCODE_d);    //зажигаю сегмент "d"
                        Indicator(ADDR_HL3,LEDCODE_d);    //зажигаю сегмент "d"
                        Indicator(ADDR_HL4,LEDCODE_d);    //зажигаю сегмент "d"
                        Indicator(ADDR_HL5,LEDCODE_d);    //зажигаю сегмент "d"
                        Indicator(ADDR_HL6,LEDCODE_d);    //зажигаю сегмент "d"
                        Indicator(ADDR_HL7,LEDCODE_d);    //зажигаю сегмент "d"
                        Indicator(ADDR_HL8,LEDCODE_d);    //зажигаю сегмент "d"
                        delay ();    //пауза
                    }
                    else if (j==4)
                    {
                        Indicator(ADDR_HL1,LEDCODE_e);    //зажигаю сегмент "e"
                        Indicator(ADDR_HL2,LEDCODE_e);    //зажигаю сегмент "e"
                        Indicator(ADDR_HL3,LEDCODE_e);    //зажигаю сегмент "e"
                        Indicator(ADDR_HL4,LEDCODE_e);    //зажигаю сегмент "e"
                        Indicator(ADDR_HL5,LEDCODE_e);    //зажигаю сегмент "e"
                        Indicator(ADDR_HL6,LEDCODE_e);    //зажигаю сегмент "e"
                        Indicator(ADDR_HL7,LEDCODE_e);    //зажигаю сегмент "e"
                        Indicator(ADDR_HL8,LEDCODE_e);    //зажигаю сегмент "e"
                        delay ();    //пауза
                    }
                    else if (j==5)
                    {
                        Indicator(ADDR_HL1,LEDCODE_f);    //зажигаю сегмент "f"
                        Indicator(ADDR_HL2,LEDCODE_f);    //зажигаю сегмент "f"
                        Indicator(ADDR_HL3,LEDCODE_f);    //зажигаю сегмент "f"
                        Indicator(ADDR_HL4,LEDCODE_f);    //зажигаю сегмент "f"
                        Indicator(ADDR_HL5,LEDCODE_f);    //зажигаю сегмент "f"
                        Indicator(ADDR_HL6,LEDCODE_f);    //зажигаю сегмент "f"
                        Indicator(ADDR_HL7,LEDCODE_f);    //зажигаю сегмент "f"
                        Indicator(ADDR_HL8,LEDCODE_f);    //зажигаю сегмент "f"
                        delay ();    //пауза
                    }
                }
            }
}


Программа работает, сегменты изменяются.
Вопросы:
1) Правильно ли я организовал программу?
2) Есть ли методы, при которых программа была бы более компактной?

Теперь на работе предложили написать программу, при которой был бы задействован какой-то вход под АЦП. На данный вход подается напряжение до 5 В и это напряжение должно индикатироваться на индикаторе. Точность – до сотой (0,01) вольта.

PIC18F6722 – содержит 12 аналоговых входов (5 входов на PORTF – AN0-AN4 и 7 входов на PORTF – AN5-AN11). Выводы МК AN0-AN4 задействованы для подключения клавиатуры через дешифратор D4, поэтому их использовать не получиться. Выводы – AN5-AN10 (ножки 13-18) используются на плате для вывода изображения на ЖКИ. Они выводятся напрямую на разъем Х10.
В качестве аналоговых входов выбрал AN5 и AN6.

Как я понимаю, при работе ЦАП необходимо опорное напряжение, относительно которого будет производиться измерение напряжения.
VREF+ (21 ножка МК) - не задействована и не подключена. Микросхема в корпусе 64-Pin TQFP. Шаг ножек очень мелкий, припаять провод проблематично.
VREF- (22 ножка МК) задействована в качестве выхода под дешифратор D4. Вывода AVDD и AVSS (ножки 19 и 20) не подключены - подпаяться тоже проблематично.

Как я понимаю, организовать ЦАП без использования этих входов невозможно, т.е. создание на этой плате устройства с применением ЦАП (измерение напряжения).

Верно?
_Артём_
Цитата(Andbiz @ Aug 21 2012, 18:40) *
Программа работает, сегменты изменяются.
Вопросы:
2) Есть ли методы, при которых программа была бы более компактной?



Код
const unsigned char AddrTable[8]=
{
    ADDR_HL1,
    ADDR_HL2,
    ADDR_HL3,
    ADDR_HL4,
    ADDR_HL5,
    ADDR_HL6,
    ADDR_HL7,
    ADDR_HL8
};

const unsigned char LedCode[7]=
{
    LEDCODE_a,
    LEDCODE_b,
    LEDCODE_c,
    LEDCODE_d,
    LEDCODE_e,
    LEDCODE_f,
    LEDCODE_g
};

void Indicator(char addrCode, char dataCode);

void main ()
{
// .....
    while (1) {

        for (unsigned char j = 0; j < 6; j++ ) {
            for (unsigned char i=0; i<sizeof(AddrTable); i++) {
                Indicator(AddrTable[i], LedCode[j]);
            }
            delay ();    //пауза
        }
    }
}
esaulenka
Маленький совет: при написании комментариев забыть про буфер обмена. Вообще совсем.
Тогда вот это
Код
#define ADDR_HL1 0x3    // определяю адрес первого индикатора HL1 0x03, как переменную ADDR_HL1
#define ADDR_HL2 0x4    // определяю адрес второго индикатора HL2 0x04, как переменную ADDR_HL2

пишется как
Код
// определяю адреса индикаторов
#define ADDR_HL1 0x3    // первый индикатор
#define ADDR_HL2 0x4    // второй индикатор

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


И также присоединяюсь к предложению почитать книжки. Классика жанра - Керниган-Ричи, но можно и полистать более "упрощённый" вариант с упором в микроконтроллеры.
_Артём_
Цитата(esaulenka @ Aug 21 2012, 19:28) *
Маленький совет: при написании комментариев забыть про буфер обмена. Вообще совсем.
Тогда вот это
Код
#define ADDR_HL1 0x3    // определяю адрес первого индикатора HL1 0x03, как переменную ADDR_HL1
#define ADDR_HL2 0x4    // определяю адрес второго индикатора HL2 0x04, как переменную ADDR_HL2

пишется как
Код
// определяю адреса индикаторов
#define ADDR_HL1 0x3    // первый индикатор
#define ADDR_HL2 0x4    // второй индикатор

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

К тому же первый вариант комментария ошибочный: ADDR_HL1 - это не переменная, и #define переменные не объявляет.
_Ivana
Да, читая комментарии я только ещё больше путаюсь в этом коде и не сразу понял подвох даже в таком простом куске с таким комментарием sm.gif
Цитата
void delay (void) //организую подпрограмму задержки
{
unsigned int i;
for (i==0; i<6000; i++); //длительность задержки опрделяется величиной i
}
esaulenka
и о ЦАПах, которые на самом деле очень даже АЦП

Цитата
при работе ЦАП необходимо опорное напряжение, относительно которого будет производиться измерение напряжения.
VREF+ (21 ножка МК) - не задействована и не подключена. Микросхема в корпусе 64-Pin TQFP. Шаг ножек очень мелкий, припаять провод проблематично. VREF- (22 ножка МК) задействована в качестве выхода под дешифратор D4.

Ну и ладно. Эти входы являются опциональными, о чём написано на второй же станице даташита в разделе АЦП. По умолчанию опорными являются AVDD и AVSS.
Точности в сотую вольта, с таким подходом добиться будет сложно (какая там у Вас точность опорного? 2%? а мерять-то хочется с точностью в 10 раз больше!) , ну да не с профессиональными же вольтметрами конкурировать sm.gif

Цитата
Вывода AVDD и AVSS (ножки 19 и 20) не подключены -

Ну это явная ошибка разработчика. Если я правильно помню микрочиповские даташиты (крайний раз работал с пиками в студенчестве), где-то была специальная схема "какие ноги минимально необходимо соединить". В их число входят ВСЕ ноги питания, т.е. в данном случае по 4 ноги VSS / VDD, а также AVSS/AVDD.

Цитата
подпаяться тоже проблематично.

Вопрос привычки. При наличии нормальной паяльной станции (надо разводить на деньги начальство! хотя бы на 100$, потом очень пригодится) работы на пару минут вместе с подбором тоненькой проволочки, нагревом паяльника и протиркой результатов спиртом sm.gif
Andbiz
_Артём_
Спасибо, понял – более компактный код можно достичь организацией матрицы, ее заполнением, а затем выводом элементов матрицы поочередно на индикаторы организацией цикла. #define – объявляет константы

esaulenka
Про более компактные комментарии понял.
Книгой Шпак «Программирование на языке С для AVR и PIC микроконтроллеров» пользовался и ранее, но только за 2011 год.
Да, с вольтметром ошибся – должен применяться АЦП. И ошибся с подключением ножек AVDD и AVSS – на самом деле они подключены к VDD и VSS. На принципиальной схеме не показаны, а на плате дорожки проведены под микросхемой и можно только определить, прозвонив прибором. Т.е. как я понял – вольтметр все же можно организовать. По поводу точности 0,01 – она на самом деле не сильно важна. Тут только учебные цели и погрешность условная. Пусть будет точность 5% - это не принципиально.

Попробую все же разобраться с вольтметром. В качестве аналогового входа для измерения напряжения, попробую использовать AN5. Выводы AVDD и AVSS подключены к +5В и GND.
В регистре ADCON1 в битах 5-4 с значениями 00 в качестве A/D VREF+ используется AVDD, а в качестве A/D VREF- используется AVSS. Т.е. как Вы и написали – по умолчанию опорными являются AVDD и AVSS (страница 272 даташита).
Т.к. опорное напряжение равно 5 В, то дискретность измерения АЦП будет равняться 5/1024 В =0, 00489 В/квант.
Т.к. АЦП будет оцифровывать напряжение от GND до AVDD, т.е. до 5 В. На всякий случай буду подавать напряжение на аналоговый вход AN5 через делитель по следующей схеме:
Резистор R2 для защиты входа МК, падением напряжения на нем принебрегаю ввиду малости тока АЦП. На резисторе R1 падение напряжения 1,65 В, поэтому максимально подаваемое напряжение на АЦП равно 3,35 В – при этом значении на входе будет 5 В и это значение нужно будет отобразить на семисегментном индикаторе. При уменьшении напряжения значение на индикаторе должно соответственно уменьшаться.

Нажмите для просмотра прикрепленного файла

Код
return ( ADRESL + ( ADRESH << 8 ) ) * 0.00489;

В работе АЦП есть такая строка. Return возвращает результат АЦП в необходимый кусок программы с необходимым значением. При этом происходит доумножение результата на 0,0489 (дискретность измерения), т.е. узнают величину напряжения в десятичной форме.
Вопросы по этой строке:
1) За return должно следовать выражение в скобках, которое может выполняться и переноситься в необходимый участок схемы. В данном случае в скобки берётся не все выражение, а только часть. Т.е. скобки ставить не обязательно и так можно делать?
2) Знак << 8 означает поразрядный сдвиг влево на 8 разрядов. Для чего это делать. Затем эти два разряда складывают. Зачем это делать?

Пока программа выглядит таким образом:

Код
#include <p18f6722.h>

#pragma config OSC = XT        // Применяю внешний кварцевый резонатор
#pragma config WDT = OFF    // Внутренний сторожевой таймер отключаю - применяется внешний, который нужно периодически обнулять сигналом ADR0

void IzmerVoltage (void)
{
    ADCON0bits.GO=1; //запускаю преобразователь АЦП, записав 1 в бит1 регистра ADCON0
    while (ADCON0bits.GO==1); //запускается цикл проверки работы АЦП. Окончание преобразование - окончание цикла
    return ((ADRESL+(ADRESH<<8))*0.00489);    
}

void main void    //точка входа в основную програму
{
    ADCON0=0x15;    //разрешаю работу АЦП (бит0=1), выбираю входа AN5 (биты2-5=0101)
    ADCON1=0x09;    //AN5-аналоговый вход (биты0-3=1001), AVDD и AVSS - опорные (биты4-5=00)
    ADCON2=0x8C;    //частота работы АЦП=1,5Мгц (биты0-2=100), время 2*Tad (биты3-5=001), результат АЦП justifield справа(бит7=1)
    
    TRISF=1;            //порт F - вход
    
    while (1)
    {
    
    Тут будет программа

    }
}


Каким образом лучше сделать соответствие между измеренным значением напряжения с АЦП в десятичной форме и индикаторами? Т.е. каким образом можно привязать целую часть и дробную часть числа к отдельным индикатором и выводить на нее необходимую комбинацию сегментов?
Ruslan1
Про индикацию:
самое простое для разборок- это
1. вычислить значение в вольтах или в тех единицах вольт, которые нужно отобразить. Это позволяет остаться в границах целочисленной арифметики. Например, делаем все в милливольтах. Тогда 16-битного целого хватит для напряжений до 65.535 В.
Код
Umv = ( UREFmV / (1<<NADC) * ADCcode) * KATT

где
UREFmV = 5000 (mV)
NADC - разрядность АЦП (у Вас это 10)
ADCcode - код прочитанный из АЦП
KATT - коэффициент ослабления аттенюатора (делителя) на входе. в Вашем случае 1.

2. переводим эту величину в посимвольное отображение в буфере
Код
char buf[5+1];  // максимальная отображаемая величина требует 5 знакомест плюс символ конца строки 0x00 в конце
sprintf (buf, "05d", Umv);

после этого число Umv будет "напечатано" в буфер buf, причем старшие незначащие цифры будут '0'. например, величина 1234 будет напечатана как "01234"

3. добавляем десятичную точку в буфер. Это позволяет дальше удобно и без хитростей выводить подготовленный буфер куда угодно.
Код
char buf2[2+1+3+1];  // 2 на целую часть, 1 на точку, 3 на дробную часть, 1 на завершающий строку символ
buf2[0]= buf[0];
buf2[1]= buf[1];
buf2[2]= '.';
buf2[3]= buf[2];
buf2[4]= buf[3];
buf2[5]= buf[4];
buf2[6]= '\0';

3. Выбираем посимвольно из буфера цифры и делаем с ними все что хотим например:

Код
const char IND_ADDR[] = {NULL, ADDR_HL1,ADDR_HL2,ADDR_HL3,};   // нулевой элемент массива не используется
for (i = 1 to 6)
{
switch (buf2[i])
{
  case '.': out = LED_CODE_DOT; break;
  case '0': out = LED_CODE_0; break;
  case '1': out = LED_CODE_1; break;
  case '2': out = LED_CODE_2; break;
  case '3': out = LED_CODE_3; break;
  default: out = LED_CODE_E; break;  // error
} // end switch()
Indicator(IND_ADDR[i], out);
}; // end for()


Я конечно помню, что всякие АЛС324 одновременно с символом могут точку светить в этом же разряде, но это уже следующий этап игры sm.gif
esaulenka
Цитата
1) За return должно следовать выражение в скобках, которое может выполняться и переноситься в необходимый участок схемы. В данном случае в скобки берётся не все выражение, а только часть. Т.е. скобки ставить не обязательно и так можно делать?

За return должно следовать выражение того же типа, которое должна возвращать функция.
В данном случае объявлена функция, которая ничего не возвращает, и писать после return ничего нельзя.
Про скобки - это такая рекомендация для удобочитаемости. Необязательная, и лично я пишу без скобок.

Цитата
2) Знак << 8 означает поразрядный сдвиг влево на 8 разрядов. Для чего это делать. Затем эти два разряда складывают. Зачем это делать?

Знак <<1 означает сдвиг влево на один разряд. Результат этого действия - значение умножается на 2. Подобная методика умножения используется в "мелких" контроллерах, которые не имеют аппаратного умножителя вовсе (или этот умножитель работает очень медленно). Аналогично можно делить (аппаратное деление - штука вообще редкая).
Выяснить, что получается при сдвиге на 8 разрядов, предлагаю самостоятельно.
Также самостоятельно предлагаю разобраться, как АЦП раскладывает результаты своей работы по регистрам ADRESL и ADRESH, и что надо сделать, чтобы собрать его в одну переменную.

Если вкратце, читать, читать и ещё раз читать.
Andbiz
Спасибо! Программу сделал. Код получился следующий:

Код
#include <p18f6722.h>
#include <stdio.h>

#pragma config OSC = XT        // Применяю внешний кварцевый резонатор
#pragma config WDT = OFF    // Внутренний сторожевой таймер отключаю - применяется внешний, который нужно периодически обнулять сигналом ADR0

#define ADDR_HL1 0x3    // определяю адрес первого индикатора HL1 0x03, как переменную ADDR_HL1
#define ADDR_HL2 0x4    // определяю адрес второго индикатора HL2 0x04, как переменную ADDR_HL2
#define ADDR_HL3 0x5    // определяю адрес третьего индикатора HL3 0x05, как переменную ADDR_HL3
#define ADDR_HL4 0x6    // определяю адрес четвертого индикатора HL4 0x06, как переменную ADDR_HL4
#define ADDR_HL5 0x7    // определяю адрес пятого индикатора HL5 0x07, как переменную ADDR_HL5
#define ADDR_HL6 0x8    // определяю адрес шестого индикатора HL6 0x08, как переменную ADDR_HL6
#define ADDR_HL7 0x9    // определяю адрес седьмого индикатора HL7 0x09, как переменную ADDR_HL7
#define ADDR_HL8 0xA    // определяю адрес восьмого индикатора HL8 0x0A, как переменную ADDR_HL8

#define LED_CODE_0 0xC0    //определяю переменную, при которой на индикаторе будет отображаться "0"
#define LED_CODE_1 0xF9    //определяю переменную, при которой на индикаторе будет отображаться "1"
#define LED_CODE_2 0xA4    //определяю переменную, при которой на индикаторе будет отображаться "2"
#define LED_CODE_3 0xB0    //определяю переменную, при которой на индикаторе будет отображаться "3"
#define LED_CODE_4 0x99    //определяю переменную, при которой на индикаторе будет отображаться "4"
#define LED_CODE_5 0x92    //определяю переменную, при которой на индикаторе будет отображаться "5"
#define LED_CODE_6 0x82    //определяю переменную, при которой на индикаторе будет отображаться "6"
#define LED_CODE_7 0xF8    //определяю переменную, при которой на индикаторе будет отображаться "7"
#define LED_CODE_8 0x80    //определяю переменную, при которой на индикаторе будет отображаться "8"
#define LED_CODE_9 0x90    //определяю переменную, при которой на индикаторе будет отображаться "9"
#define LED_CODE_E 0x86    //определяю переменную, при которой на индикаторе будет отображаться "E"
#define LEDCODE_X 0xFF    //определяю переменную, при которой на индикаторе не будет отображаться ни один сегмент

#define PORT_DATA PORTD    //определяю PORTD, как переменную PORT_DATA
#define PORT_ADDR PORTE    //определяю PORTE, как переменную PORT_ADDR

void Indicator(char addrCode, char dataCode);    //определяю функцию с именем Indicator, char - символьный тип данных

void Indicator(char addrCode, char dataCode)    //ввожу параметры функции, т.е. значения, которые будут находиться под именам addrCode и dataCode.
{
    PORT_ADDR = addrCode;    //под параметром addrCode будет значение переменной PORT_ADDR
    PORT_DATA = dataCode;    //под параметром dataCode будет значение переменной PORT_DATA
}

char i;                //переменная i - счетчик перебора индикаторов в основной программе
char out[7];
int UREFmv, ADCcode, Umv;    //переменная "UREFmv"-величина опорного напряжения
char NADC, KATT;        //переменная "NADC"-разрядность АЦП (у PIC18F6722 - 10 bit); переменная "KATT"-коээфициент делителя (ослабления аттенюатора) на входе
float volts;            //переменная "volts" - с плавающей запятой
char buf[5+1];            //организую первый буфер для результата с АЦПр: максимальная отображаемая величина требует 5 знакомест + символ конца строки 0х00 в конце    
char buf2[2+1+3+1];    //организую второй буфер: 2 на целую часть, 1 на точку, 3 - на друбную часть, 1 - на завершающий строку символ
const char IND_ADDR[]={0,ADDR_HL1,ADDR_HL1,ADDR_HL2,ADDR_HL3,ADDR_HL4};    //организую массив, каждый элемент которого - адрес индикатора, нулевой элемент массива не используется, const - размещаю в флеш-памяти МК

#pragma code        //произвожу запись дальнейших данных в память программ

int IzmerVoltage (void)    //организую подпрограмму запуска АЦП с результатом с плавающей запятой
{
    ADCON0bits.GO=1; //запускаю преобразователь АЦП, записав 1 в бит1 регистра ADCON0
    while (ADCON0bits.GO==1); //запускается цикл проверки работы АЦП. Окончание преобразование - окончание цикла
    return ADRES*4.89;
}

void main (void)    //точка входа в основную програму
{
    ADCON0=0x15;    //разрешаю работу АЦП (бит0=1), выбираю входа AN5 (биты2-5=0101)
    ADCON1=0x09;    //AN5-аналоговый вход (биты0-3=1001), AVDD и AVSS - опорные (биты4-5=00)
    ADCON2=0x8C;    //частота работы АЦП=1,5Мгц (биты0-2=100), время 2*Tad (биты3-5=001), результат АЦП justifield справа(бит7=1)
    
    TRISD=0;    //порт D - выход
    TRISE=0;        //порт E - выход
    TRISF=1;        //порт F - вход

    Indicator(ADDR_HL1,LEDCODE_X);    //сбрасываю индикатор HL1
    Indicator(ADDR_HL2,LEDCODE_X);    //сбрасываю индикатор HL2
    Indicator(ADDR_HL3,LEDCODE_X);    //сбрасываю индикатор HL3
    Indicator(ADDR_HL4,LEDCODE_X);    //сбрасываю индикатор HL4
    Indicator(ADDR_HL5,LEDCODE_X);    //сбрасываю индикатор HL5
    Indicator(ADDR_HL6,LEDCODE_X);    //сбрасываю индикатор HL6
    Indicator(ADDR_HL7,LEDCODE_X);    //сбрасываю индикатор HL7
    Indicator(ADDR_HL8,LEDCODE_X);    //сбрасываю индикатор HL8

    while (1)        //главный цикл    
    {
            Umv=IzmerVoltage();                        
            sprintf (buf, "%05d", Umv);            //помещение значения с АЦП Umv в милливольтах в буфер buf, старшие незначащие цифры будут "0" (пример: "1234" - "01234")
            buf2[0]=buf[0];        //в нулевой элемент буфера buf2 ввожу нулевой элемент буфера buf
            buf2[1]=buf[1];        //в первый элемент буфера buf2 ввожу первый элемент буфера buf
            buf2[2]='.';        //в второй элемент буфера buf2 ввожу точку "."
            buf2[3]=buf[2];        //в третий элемент буфера buf2 ввожу второй элемент буфера buf
            buf2[4]=buf[3];        //в четвертый элемент буфера buf2 ввожу третий элемент буфера buf
            buf2[5]=buf[4];        //в пятый элемент буфера buf2 ввожу четвертый элемент буфера buf
            buf2[6]='\0';        //в шестой элемент буфера buf2 ввожу символ конца строки 0х00
                for (i=1;i<5;i=i+1)    //организую цикл перебора шести индикаторов и заполнения их 6 элементами массива buf2 (кроме седьмого - нулевого)
                {
                    switch (buf2[i])                    //организую перебор содержимого буфера buf2 и сравнение с символами, которые нужно выводить на индикаторы
                    {
case '.':out[i]=out[i-1]+0x80; break;    //если в массиве точка - вывожу в предыдущий индикатор точку и выход из цикла сравнения элемента, переход к следующему (break)
case '0':out[i]=LED_CODE_0; break;    //если в массиве ноль - выводить на индикатор ноль и выход из цикла сравнения элемента, переход к следующему (break)
case '1':out[i]=LED_CODE_1; break;    //если в массиве один - выводить на индикатор один и выход из цикла сравнения элемента, переход к следующему (break)
case '2':out[i]=LED_CODE_2; break;    //если в массиве два - выводить на индикатор два и выход из цикла сравнения элемента, переход к следующему (break)
case '3':out[i]=LED_CODE_3; break;    //если в массиве три - выводить на индикатор три и выход из цикла сравнения элемента, переход к следующему (break)
case '4':out[i]=LED_CODE_4; break;    //если в массиве четыре - выводить на индикатор четыре и выход из цикла сравнения элемента, переход к следующему (break)
case '5':out[i]=LED_CODE_5; break;    //если в массиве пять - выводить на индикатор пять и выход из цикла сравнения элемента, переход к следующему (break)
case '6':out[i]=LED_CODE_6; break;    //если в массиве шесть - выводить на индикатор шесть и выход из цикла сравнения элемента, переход к следующему (break)
case '7':out[i]=LED_CODE_7; break;    //если в массиве семь - выводить на индикатор семь и выход из цикла сравнения элемента, переход к следующему (break)
case '8':out[i]=LED_CODE_8; break;    //если в массиве восемь - выводить на индикатор восемь и выход из цикла сравнения элемента, переход к следующему (break)
case '9':out[i]=LED_CODE_9; break;    //если в массиве девять - выводить на индикатор девять и выход из цикла сравнения элемента, переход к следующему (break)
default: out[i]=LED_CODE_E; break;    //в противном случае - высвечивать индикацию ошибки "Е" и переход к следующему элементу (break)
                    }                                    //окончание перебора содержимого буфера buf2
                    Indicator(IND_ADDR[i],out[i]);        //вывести полученное значение на необходимый индикатор
                };                //конец цикла перебора
    }
}


Теперь пробую написать программу, связанную с вводом цифр с клавиатуры 3х6 на семисегментный индикатор и выполнением простейших арифметических операций – сложение, вычитание, умножение и деление. Использоваться будут 4 индикатора. Также хочеться сделать ввод дробных чисел, т.е. ставить точку.

Сначала хотелось бы просто реализовать ввод чисел с клавиатуры и отображением их на экране (без точки). Контакты на клавиатуре – нормально открытые.
Матрица получилась следующая:

Нажмите для просмотра прикрепленного файла

На столбцы сигналы подаются через дешифратор D4. На дешифратор подаются сигналы А0, А1, А2 с выходов PORTA – RA0, RA1, RA2 (ножки 24, 23, 22). Сигналы с строк считываются через сигналы INT0, INT1, INT2, которые подаются на входа PORTB – RB0, RB1, RB2 (ножки 48, 47, 46).
Текст программы пока вот такой:

Код
#include <p18f6722.h>

#pragma config OSC = XT        // Применяю внешний кварцевый резонатор
#pragma config WDT = OFF    // Внутренний сторожевой таймер отключаю - применяется внешний, который нужно периодически обнулять сигналом ADR0
#pragma config MCLRE = OFF    // отключаю RESEЕ, данный вывод используется, как RG5

#define ADDR_HL1 0x3    // определяю адрес первого индикатора HL1 0x03, как переменную ADDR_HL1
#define ADDR_HL2 0x4    // определяю адрес второго индикатора HL2 0x04, как переменную ADDR_HL2
#define ADDR_HL3 0x5    // определяю адрес третьего индикатора HL3 0x05, как переменную ADDR_HL3
#define ADDR_HL4 0x6    // определяю адрес четвертого индикатора HL4 0x06, как переменную ADDR_HL4
#define ADDR_HL5 0x7    // определяю адрес пятого индикатора HL5 0x07, как переменную ADDR_HL5
#define ADDR_HL6 0x8    // определяю адрес шестого индикатора HL6 0x08, как переменную ADDR_HL6
#define ADDR_HL7 0x9    // определяю адрес седьмого индикатора HL7 0x09, как переменную ADDR_HL7
#define ADDR_HL8 0xA    // определяю адрес восьмого индикатора HL8 0x0A, как переменную ADDR_HL8

#define LED_CODE_0 0xC0        //определяю переменную, при которой на индикаторе будет отображаться "0"
#define LED_CODE_1 0xF9        //определяю переменную, при которой на индикаторе будет отображаться "1"
#define LED_CODE_2 0xA4        //определяю переменную, при которой на индикаторе будет отображаться "2"
#define LED_CODE_3 0xB0        //определяю переменную, при которой на индикаторе будет отображаться "3"
#define LED_CODE_4 0x99        //определяю переменную, при которой на индикаторе будет отображаться "4"
#define LED_CODE_5 0x92        //определяю переменную, при которой на индикаторе будет отображаться "5"
#define LED_CODE_6 0x82        //определяю переменную, при которой на индикаторе будет отображаться "6"
#define LED_CODE_7 0xF8        //определяю переменную, при которой на индикаторе будет отображаться "7"
#define LED_CODE_8 0x80        //определяю переменную, при которой на индикаторе будет отображаться "8"
#define LED_CODE_9 0x90        //определяю переменную, при которой на индикаторе будет отображаться "9"
#define LED_CODE_X 0xFF        //определяю переменную, при которой на индикаторе не будет отображаться ни один сегмент

#define PORT_DATA LATD        //определяю LATD, как переменную PORT_DATA
#define PORT_ADDR LATE        //определяю LATE, как переменную PORT_ADDR
#define NUMBER_COLOWN LATA    //определяю LATA, как переменную NUMBER_COLOWN
#define NUMBER_ROW LATB    //определяю LATB, как переменную NUMBER_ROW

void Indicator(char addrCode, char dataCode);    //определяю функцию с именем Indicator, char - символьный тип данных

void Indicator(char addrCode, char dataCode)    //ввожу параметры функции, т.е. значения, которые будут находиться под именам addrCode и dataCode.
{
    PORT_ADDR = addrCode;    //под параметром addrCode будет значение переменной PORT_ADDR
    PORT_DATA = dataCode;    //под параметром dataCode будет значение переменной PORT_DATA
}

char out, i;
char SIMVOL;
char buf[4+1];        //первый буфер для ввода чисел: 4 знака + символ конца строки 0х00 в конце
const int IND_ADDR[]={0,ADDR_HL4,ADDR_HL3,ADDR_HL2,ADDR_HL1};    //массив, элементы-индикаторы, нулевой элемент-не используется

void delay (void)        //организую подпрограмму задержки
{
    unsigned int i;
    for (i==0; i<10000; i++);    //длительность задержки опрделяется величиной i
}

int Nomer_klavishi (void) //Определение символа нажатой клавиши
{

TRISA=0xF8;                    //порт A - выход
TRISB=0xFF;                    //порт В - вход
ADCON1=0xF;                    //отключаю АЦП, все выводы - цифровые
SIMVOL=0;                    //значение переменной при отсутствии нажатии клавиши

    NUMBER_COLOWN=0x00;                            //выдаю 0 на столбец 1
    if (PORTBbits.RB0==0) SIMVOL=1;                //проверяю 0 на строке 1
    else if (PORTBbits.RB1==0) SIMVOL=2;        //проверяю 0 на строке 2
         else if (PORTBbits.RB2==0) SIMVOL=3;    //проверяю 0 на строке 3
    NUMBER_COLOWN=0x01;                            //выдаю 0 на столбец 2
    if (PORTBbits.RB0==0) SIMVOL=4;                //проверяю 0 на строке 1
    else if (PORTBbits.RB1==0) SIMVOL=5;        //проверяю 0 на строке 2
         else if (PORTBbits.RB2==0) SIMVOL=6;    //проверяю 0 на строке 3
    NUMBER_COLOWN=0x02;                            //выдаю 0 на столбец 3
    if (PORTBbits.RB0==0) SIMVOL=7;                //проверяю 0 на строке 1
    else if (PORTBbits.RB1==0) SIMVOL=8;        //проверяю 0 на строке 2
         else if (PORTBbits.RB2==0) SIMVOL=9;    //проверяю 0 на строке 3
    NUMBER_COLOWN=0x03;                            //выдаю 0 на столбец 4
    if (PORTBbits.RB0==0) SIMVOL=10;            //проверяю 0 на строке 1
    else if (PORTBbits.RB1==0) SIMVOL=11;        //проверяю 0 на строке 2
         else if (PORTBbits.RB2==0) SIMVOL=12;    //проверяю 0 на строке 3
    NUMBER_COLOWN=0x04;                            //выдаю 0 на столбец 5
    if (PORTBbits.RB0==0) SIMVOL=13;            //проверяю 0 на строке 1
    else if (PORTBbits.RB1==0) SIMVOL=14;        //проверяю 0 на строке 2
         else if (PORTBbits.RB2==0) SIMVOL=15;    //проверяю 0 на строке 3
    NUMBER_COLOWN=0x05;                            //выдаю 0 на столбец 6
    if (PORTBbits.RB0==0) SIMVOL=16;            //проверяю 0 на строке 1
    else if (PORTBbits.RB1==0) SIMVOL=17;        //проверяю 0 на строке 2
         else if (PORTBbits.RB2==0) SIMVOL=18;    //проверяю 0 на строке 3
    return (SIMVOL);
}

void main (void)    //точка входа в основную програму
{

    TRISD=0;        //порт D - выход
    TRISE=0;        //порт E - выход

    Indicator(ADDR_HL1,LED_CODE_X);    //сбрасываю индикатор HL1
    Indicator(ADDR_HL2,LED_CODE_X);    //сбрасываю индикатор HL2
    Indicator(ADDR_HL3,LED_CODE_X);    //сбрасываю индикатор HL3
    Indicator(ADDR_HL4,LED_CODE_X);    //сбрасываю индикатор HL4
    Indicator(ADDR_HL5,LED_CODE_X);    //сбрасываю индикатор HL5
    Indicator(ADDR_HL6,LED_CODE_X);    //сбрасываю индикатор HL6
    Indicator(ADDR_HL7,LED_CODE_X);    //сбрасываю индикатор HL7
    Indicator(ADDR_HL8,LED_CODE_X);    //сбрасываю индикатор HL8

    i=1;    //номер индикатора, в который будет вводиться символ с клавиатуры
    out=LED_CODE_X;    //пустое значение

    while (1)        //главный цикл
    {
        SIMVOL=Nomer_klavishi();


            if  (SIMVOL!=0)
                Indicator(IND_ADDR[i+1],out);    //вывожу ранее полученное значение на следующий индикатор, если раньше не вводилось, то ввожу пустоту
            if  (SIMVOL!=0)    switch (SIMVOL)                 //начинаю цикл перебора символа для вывода на экран
                {
                case 1:out=LED_CODE_7; break;        //загорается семь
                case 2:out=LED_CODE_4; break;        //загорается четыре
                case 3:out=LED_CODE_1; break;        //загорается один
                case 4:out=LED_CODE_8; break;        //загорается восемь
                case 5:out=LED_CODE_5; break;        //загорается пять
                case 6:out=LED_CODE_2; break;        //загорается два
                case 7:out=LED_CODE_9; break;        //загорается девять
                case 8:out=LED_CODE_6; break;        //загорается шесть
                case 9:out=LED_CODE_0; break;        //загорается ноль
                case 12:out=LED_CODE_3; break;        //загорается три
                default:out=LED_CODE_X; break;        //ничего не загорается
                };

                if (SIMVOL!=0) Indicator(IND_ADDR[i],out);    //вывожу полученное значение на индикатор
                if (SIMVOL!=0) i=i+1;                        //если ранее был введен символ с клавиатуры, то следующий вводиться в следующий индикатор
    delay ();        //пауза
    }
}


При вводе цифр с клавиатуры текст вводиться, но ввод осуществляется справа на лево, т.е. сначала цифра вводиться в HL4, HL3, HL2 и затем в HL1. Хочется реализовать ввод, как в обычном калькуляторе, т.е. число вводиться в правый сегмент, а все введенные ранее элементы синхронно сдвигаются влево. Слышал, что есть операция memcpy и ею можно реализовать сдвиг, слышал про то, что можно использовать тип переменной long int и организовать побайтный сдвиг влево. Думаю, что можно осуществить это же ввод на цикле условия «if», но будет очень большой текст этого участка программы. Какой способ лучше выбрать?
Andbiz
Программу для ввода текста сделал – вводит все значения, включая точку нормально.
Возможно выглядит не сильно красиво и правильно, но ввод чисел с клавиатуры включая запятую работает.

Код
#include <p18f6722.h>

#pragma config OSC = XT    // Применяю внешний кварцевый резонатор
#pragma config WDT = OFF    // Внутренний сторожевой таймер отключаю - применяется внешний, который нужно периодически обнулять сигналом ADR0
#pragma config MCLRE = OFF    // отключаю RESEЕ, данный вывод используется, как RG5

#define ADDR_HL1 0x3    // определяю адрес первого индикатора HL1 0x03, как переменную ADDR_HL1
#define ADDR_HL2 0x4    // определяю адрес второго индикатора HL2 0x04, как переменную ADDR_HL2
#define ADDR_HL3 0x5    // определяю адрес третьего индикатора HL3 0x05, как переменную ADDR_HL3
#define ADDR_HL4 0x6    // определяю адрес четвертого индикатора HL4 0x06, как переменную ADDR_HL4
#define ADDR_HL5 0x7    // определяю адрес пятого индикатора HL5 0x07, как переменную ADDR_HL5
#define ADDR_HL6 0x8    // определяю адрес шестого индикатора HL6 0x08, как переменную ADDR_HL6
#define ADDR_HL7 0x9    // определяю адрес седьмого индикатора HL7 0x09, как переменную ADDR_HL7
#define ADDR_HL8 0xA    // определяю адрес восьмого индикатора HL8 0x0A, как переменную ADDR_HL8

#define LED_CODE_0 0xC0        //определяю переменную, при которой на индикаторе будет отображаться "0"
#define LED_CODE_1 0xF9        //определяю переменную, при которой на индикаторе будет отображаться "1"
#define LED_CODE_2 0xA4        //определяю переменную, при которой на индикаторе будет отображаться "2"
#define LED_CODE_3 0xB0        //определяю переменную, при которой на индикаторе будет отображаться "3"
#define LED_CODE_4 0x99        //определяю переменную, при которой на индикаторе будет отображаться "4"
#define LED_CODE_5 0x92        //определяю переменную, при которой на индикаторе будет отображаться "5"
#define LED_CODE_6 0x82        //определяю переменную, при которой на индикаторе будет отображаться "6"
#define LED_CODE_7 0xF8        //определяю переменную, при которой на индикаторе будет отображаться "7"
#define LED_CODE_8 0x80        //определяю переменную, при которой на индикаторе будет отображаться "8"
#define LED_CODE_9 0x90        //определяю переменную, при которой на индикаторе будет отображаться "9"
#define LED_CODE_X 0xFF        //определяю переменную, при которой на индикаторе не будет отображаться ни один сегмент

#define PORT_DATA LATD        //определяю LATD, как переменную PORT_DATA
#define PORT_ADDR LATE        //определяю LATE, как переменную PORT_ADDR
#define NUMBER_COLOWN LATA    //определяю LATA, как переменную NUMBER_COLOWN
#define NUMBER_ROW LATB    //определяю LATB, как переменную NUMBER_ROW

void Deistvie_HighInt (void);            //в программе применяется высокоприоритетное прерывание

void Indicator(char addrCode, char dataCode);    //определяю функцию с именем Indicator, char - символьный тип данных

void Indicator(char addrCode, char dataCode)    //ввожу параметры функции, т.е. значения, которые будут находиться под именам addrCode и dataCode.
{
    PORT_ADDR = addrCode;    //под параметром addrCode будет значение переменной PORT_ADDR
    PORT_DATA = dataCode;    //под параметром dataCode будет значение переменной PORT_DATA
}
char i,n;
char buf[6];
char out[6];
char SIMVOL;
char Deistvie;
char buf2[6];

#pragma interrupt Deistvie_HighInt    //Deistvie_HighInt - это прерывание
#pragma code high_vector=0x08        //high_vector - это вектор по адресу 0x08

void high_vector (void)
{
_asm GOTO Deistvie_HighInt _endasm    //переход на функцию обработки высокоприоритетного прерывания
}

void vvod_pervogo_4isla (void) //Определение символа нажатой клавиши
{
                unsigned int i;
                for (i=1;i<5;i=i+1)    //организую цикл перебора шести элементов buf для преобразования их в десятичный код
                        {
                            switch (buf[i])                        //организую перебор содержимого буфера buf и замена на десятичные знчения
                                    {
                                    case 0x7F:buf[i]='.'; break;    //замена на точку
                                    case 0xC0:buf[i]=0; break;    //замена на ноль
                                    case 0xF9:buf[i]=1; break;    //замена на один
                                    case 0xA4:buf[i]=2; break;    //замена на два
                                    case 0xB0:buf[i]=3; break;    //замена на три
                                    case 0x99:buf[i]=4; break;    //замена на четыре
                                    case 0x92:buf[i]=5; break;    //замена на пять
                                    case 0x82:buf[i]=6; break;    //замена на шесть
                                    case 0xF8:buf[i]=7; break;    //замена на семь
                                    case 0x80:buf[i]=8; break;    //замена на восемь
                                    case 0x90:buf[i]=9; break;    //замена на девять
                                    case 0xFF:buf[i]=0; break;    //пустоту также заменяю на ноль
                                    }
                        };
                sprintf (buf2, "%05d", buf);            //вношу полученные значения из буфера buf в буфер buf2, старшие незначащие цифры будут "0" (пример: "1234" - "01234")
                
                buf[0]=LED_CODE_X;    //пустое значение
                buf[1]=LED_CODE_X;    //пустое значение
                buf[2]=LED_CODE_X;    //пустое значение
                buf[3]=LED_CODE_X;    //пустое значение
                buf[4]=LED_CODE_X;    //пустое значение
}

void Deistvie_HighInt (void)
{
TRISA=0xF8;                    //порт A - выход
TRISB=0xFF;                    //порт В - вход
ADCON1=0xF;                    //отключаю АЦП, все выводы - цифровые

    if (NUMBER_COLOWN==0x03)    //проверяю 0 на столбце 4
        {
        if (PORTBbits.RB1==0) //проверяю 0 на строке 2 (плюс)
                {
                vvod_pervogo_4isla(); //переношу число из буфера buf в buf2, преобразовав его в десятичный код, очищаю buf
                Deistvie=1;            //Действие 1 - это сложение
                }
        else if (PORTBbits.RB0==0) //проверяю 0 на строке 1 (минус)
                {
                vvod_pervogo_4isla(); //переношу число из буфера buf в buf2, преобразовав его в десятичный код, очищаю buf
                Deistvie=2;            //Действие 2 - это вычитание
                }
        }
    else if (NUMBER_COLOWN==0x05)    //проверяю 0 на столбце 6
        {
        if (PORTBbits.RB2==0) //проверяю 0 на строке 3 (равно)
                {
                if (Deistvie==1)
                                {
                            //    Тут складываю два буфера (одномерных массивов)числа + вывод на индикацию
                                }
                else if (Deistvie==2)
                                {
                            //    Тут вычитаю два числа + вывод на индикацию
                                }
                }
        }
}

void delay (void)        //организую подпрограмму задержки
{
    unsigned int i;
    for (i==0; i<10000; i++);    //длительность задержки опрделяется величиной i
}

int Nomer_klavishi (void) //Определение символа нажатой клавиши
{

TRISA=0xF8;                    //порт A - выход
TRISB=0xFF;                    //порт В - вход
ADCON1=0xF;                    //отключаю АЦП, все выводы - цифровые
SIMVOL=0;                    //значение переменной при отсутствии нажатии клавиши

    NUMBER_COLOWN=0x00;                //выдаю 0 на столбец 1
    if (PORTBbits.RB0==0) SIMVOL=1;            //проверяю 0 на строке 1 (семь)
    else if (PORTBbits.RB1==0) SIMVOL=2;        //проверяю 0 на строке 2 (четыре)
         else if (PORTBbits.RB2==0) SIMVOL=3;    //проверяю 0 на строке 3 (один)
    NUMBER_COLOWN=0x01;                //выдаю 0 на столбец 2
    if (PORTBbits.RB0==0) SIMVOL=4;            //проверяю 0 на строке 1 (восемь)
    else if (PORTBbits.RB1==0) SIMVOL=5;        //проверяю 0 на строке 2 (пять)
         else if (PORTBbits.RB2==0) SIMVOL=6;    //проверяю 0 на строке 3 (два)
    NUMBER_COLOWN=0x02;                //выдаю 0 на столбец 3
    if (PORTBbits.RB0==0) SIMVOL=7;            //проверяю 0 на строке 1 (девять)
    else if (PORTBbits.RB1==0) SIMVOL=8;        //проверяю 0 на строке 2 (шесть)
         else if (PORTBbits.RB2==0) SIMVOL=9;    //проверяю 0 на строке 3 (ноль)
    NUMBER_COLOWN=0x03;                //выдаю 0 на столбец 4
    if (PORTBbits.RB0==0) SIMVOL=10;            //проверяю 0 на строке 1 (минус)
    else if (PORTBbits.RB1==0) SIMVOL=11;        //проверяю 0 на строке 2 (плюс)
         else if (PORTBbits.RB2==0) SIMVOL=12;    //проверяю 0 на строке 3 (три)
    NUMBER_COLOWN=0x04;                //выдаю 0 на столбец 5
    if (PORTBbits.RB0==0) SIMVOL=13;            //проверяю 0 на строке 1 (разделить)
    else if (PORTBbits.RB1==0) SIMVOL=14;        //проверяю 0 на строке 2 (умножить)
         else if (PORTBbits.RB2==0) SIMVOL=15;    //проверяю 0 на строке 3 (запятая)
    NUMBER_COLOWN=0x05;                //выдаю 0 на столбец 6
    if (PORTBbits.RB0==0) SIMVOL=16;            //проверяю 0 на строке 1 (не задействован)
    else if (PORTBbits.RB1==0) SIMVOL=17;        //проверяю 0 на строке 2 (не задействован)
         else if (PORTBbits.RB2==0) SIMVOL=18;    //проверяю 0 на строке 3 (равно)
    return (SIMVOL);
}

void main (void)    //точка входа в основную програму
{
    INTCONbits.RBIE=1;                //разрешаю прерывание при изменении порта RB (RB0-RB3)
    INTCON2bits.RBIP=1;                //прерывание при изменении порта RB (RB0-RB3) имеет высокий приоритет
    RCONbits.IPEN=1;                //разрешаю приоритеты прерываний
    INTCONbits.GIEL=0;                //запрещаю низкоприоритетные прерывания
    INTCONbits.GIEH=1;                //разрешаю высокоприоритетные прерывания
    
    TRISD=0;                        //порт D - выход
    TRISE=0;                        //порт E - выход

    Indicator(ADDR_HL1,LED_CODE_X);    //сбрасываю индикатор HL1
    Indicator(ADDR_HL2,LED_CODE_X);    //сбрасываю индикатор HL2
    Indicator(ADDR_HL3,LED_CODE_X);    //сбрасываю индикатор HL3
    Indicator(ADDR_HL4,LED_CODE_X);    //сбрасываю индикатор HL4
    Indicator(ADDR_HL5,LED_CODE_X);    //сбрасываю индикатор HL5
    Indicator(ADDR_HL6,LED_CODE_X);    //сбрасываю индикатор HL6
    Indicator(ADDR_HL7,LED_CODE_X);    //сбрасываю индикатор HL7
    Indicator(ADDR_HL8,LED_CODE_X);    //сбрасываю индикатор HL8

    i=1;            //номер индикатора, в который будет вводиться символ с клавиатуры
    Deistvie=0;        //номер выполняемого математического дейстия

    out[1]=LED_CODE_X;    //пустое значение
    out[2]=LED_CODE_X;    //пустое значение
    out[3]=LED_CODE_X;    //пустое значение
    out[4]=LED_CODE_X;    //пустое значение
    out[5]=LED_CODE_X;    //пустое значение

    buf[0]=LED_CODE_X;    //пустое значение
    buf[1]=LED_CODE_X;    //пустое значение
    buf[2]=LED_CODE_X;    //пустое значение
    buf[3]=LED_CODE_X;    //пустое значение
    buf[4]=LED_CODE_X;    //пустое значение

    while (1)        //главный цикл
    {
        SIMVOL=Nomer_klavishi();

            if  (SIMVOL!=0)    switch (SIMVOL)             //начинаю цикл перебора символа для вывода на экран
                {
                case 15:out[i]=out[i-1]+0x80; break;    //загорается точка
                case 1:out[i]=LED_CODE_7; break;        //загорается семь
                case 2:out[i]=LED_CODE_4; break;        //загорается четыре
                case 3:out[i]=LED_CODE_1; break;        //загорается один
                case 4:out[i]=LED_CODE_8; break;        //загорается восемь
                case 5:out[i]=LED_CODE_5; break;        //загорается пять
                case 6:out[i]=LED_CODE_2; break;        //загорается два
                case 7:out[i]=LED_CODE_9; break;        //загорается девять
                case 8:out[i]=LED_CODE_6; break;        //загорается шесть
                case 9:out[i]=LED_CODE_0; break;        //загорается ноль
                case 12:out[i]=LED_CODE_3; break;        //загорается три
                default:SIMVOL=0; break;            //ничего не загорается
                };

                if  (SIMVOL!=0)         //вывод числа на экран с сдвигом
                    {
                    if  (out[i]==out[i-1]+0x80)    //если введена точка
                                {
                                Indicator(ADDR_HL4,out[i]);        //вывожу точку
                                buf[i]=0x7F;            //в буфер заношу точку
                                if (i==2) out[1]=LED_CODE_X;    //в случае, если точка после первой цифры, то гашу первый сегмент
                                else if (i==3)             //в случае, если точка после второй цифры, то гашу второй + сдвигаю первый
                                        {
                                        out[2]=out[1];
                                        out[1]=LED_CODE_X;
                                        }    
                                else if (i==4)                     //в случае, если точка после третей цифры, то гашу третью + сдвигаю первый,второй
                                        {
                                        out[3]=out[2];
                                        out[2]=out[1];
                                        out[1]=LED_CODE_X;
                                        };
                                i=i+1;                        //следующее число сдвигает результат на экране
                                }
                    else if (i==1)
                                {
                                Indicator(ADDR_HL1,LED_CODE_X);    //пустое значение
                                Indicator(ADDR_HL2,LED_CODE_X);    //пустое значение
                                   Indicator(ADDR_HL3,LED_CODE_X);    //пустое значение
                                Indicator(ADDR_HL4,out[i]);        //значение 1
                                buf[0]=out[i];
                                i=i+1;                        //следующее число сдвигает результат на экране
                                }
                    else if (i==2)
                                {
                                Indicator(ADDR_HL1,LED_CODE_X);    //пустое значение
                                Indicator(ADDR_HL2,LED_CODE_X);    //пустое значение
                                   Indicator(ADDR_HL3,out[1]);     //значение 1
                                Indicator(ADDR_HL4,out[i]);        //значение 2
                                buf[1]=out[i];
                                i=i+1;                            //следующее число сдвигает результат на экране
                                }
                    else if (i==3)
                                {
                                Indicator(ADDR_HL1,LED_CODE_X);    //пустое значение
                                Indicator(ADDR_HL2,out[1]);        //значение 1
                                   Indicator(ADDR_HL3,out[2]);        //значение 2
                                Indicator(ADDR_HL4,out[i]);        //значение 3
                                buf[2]=out[i];
                                i=i+1;                            //следующее число сдвигает результат на экране
                                }
                    else if (i==4)
                                {
                                Indicator(ADDR_HL1,out[1]);        //значение 1
                                Indicator(ADDR_HL2,out[2]);        //значение 2
                                   Indicator(ADDR_HL3,out[3]);        //значение 3
                                Indicator(ADDR_HL4,out[i]);        //значение 4
                                buf[3]=out[i];
                                i=i+1;                            //следующее число сдвигает результат на экране
                                }
                    else if (i==5)
                                {
                                Indicator(ADDR_HL1,out[2]);     //пустое значение
                                Indicator(ADDR_HL2,out[3]);        //значение 1
                                   Indicator(ADDR_HL3,out[4]);        //значение 2
                                Indicator(ADDR_HL4,out[5]);        //значение 3
                                buf[4]=out[i];
                                i=i+1;                            //следующее число сдвигает результат на экране
                                };
                    }
    delay ();        //пауза
    }
}


Теперь пробую осуществить сложение и вычитание. Для этого в тексте программы создал буфер buf, куда вводяться временно значения первого числа, после нажатия кнопки «+» или «-» должно произойти прерывание, буфер buf должен преобразоваться в десятичную форму записи и переписать свои значения в буфер buf2. Затем буфер buf обнуляется и производиться пометка переменной «Deistvie», что после нажатия кнопки «Равно» необходимо выполнить сложение или вычитание. Затем происходит возврат к процессу ввода числа в буфер buf уже второго числа.
Прерывания организовал по сигналам на выводах R0-RB2. При этом прерывание происходить будет каждый раз, при нажатии на любую клавишу, но цикл в прерывании будет выполняться только в случае нажатие кнопок «+», «-» или «Равно».
На деле прерывание почему-то не происходит. Раньше с прерывания сталкиваться не приходилось. Из-за чего прерывание может не работать?
Andbiz
Вопрос еще по поводу функции sprintf .
Могу ли я из элементов массива (к примеру [0],[5],[8],[9],[7],[1]) создать число 58971 записью
sprintf (a, "%06d", buf);
?
esaulenka
Вот зачем, интересно, мы тут кучу советов надавали?..
И ещё раз...

Цитата
// определяю адрес первого индикатора HL1 0x03, как переменную ADDR_HL1

Во-первых, терминология неверная.
Во-вторых, комментарии "масло масляное по той причине, что оно масляное" э-э... несколько мешают восприятию.


Цитата
char i,n;

Глобальные переменные так называть НЕЛЬЗЯ. Для глобальной переменной из её названия должно быть очевидно, что там внутри находится.
Для локальной это не так критично, хотя тоже некрасиво.

Цитата
if (PORTBbits.RB0==0) SIMVOL=4; //проверяю 0 на строке 1 (восемь)
else if (PORTBbits.RB1==0) SIMVOL=5; //проверяю 0 на строке 2 (пять)
else if (PORTBbits.RB2==0) SIMVOL=6; //проверяю 0 на строке 3 (два)

Ну вот откуда очевидно, что SIMVOL=4 - это восемь, а SIMVOL=6 - это два?! Это явное место для применения дефайнов (или, что лучше, enum'а).


Цитата
for (i==0; i<10000; i++); //длительность задержки опрделяется величиной i

Как уже говорили, длительность ЭТОЙ задержки определяется погодой на Марсе. Кроме того, непонятна необходимость этой задержки в принципе.

Цитата
Могу ли я из элементов массива (к примеру [0],[5],[8],[9],[7],[1]) создать число 58971 записью
sprintf (a, "%06d", buf);

Нет, конечно. Посмотрите на типы аргументов у sprintf. Для этой задачи подойдёт sscanf, но в данном случае проще вычислять значение прямо при вводе.
Andbiz
Цитата(esaulenka @ Sep 6 2012, 14:26) *
Вот зачем, интересно, мы тут кучу советов надавали?..
Во-первых, терминология неверная.
Во-вторых, комментарии "масло масляное по той причине, что оно масляное" э-э... несколько мешают восприятию.
Глобальные переменные так называть НЕЛЬЗЯ. Для глобальной переменной из её названия должно быть очевидно, что там внутри находится.
Для локальной это не так критично, хотя тоже некрасиво.
Ну вот откуда очевидно, что SIMVOL=4 - это восемь, а SIMVOL=6 - это два?! Это явное место для применения дефайнов (или, что лучше, enum'а).

На работе у меня Интернета нет, исправления внесу на работе, там же можно сразу проверить.

Цитата(esaulenka @ Sep 6 2012, 14:26) *
Как уже говорили, длительность ЭТОЙ задержки определяется погодой на Марсе. Кроме того, непонятна необходимость этой задержки в принципе.


При нажатии клавиши может отобразиться много цифр на индикаторе. В процессе написания программы пока помогает.

Цитата(esaulenka @ Sep 6 2012, 14:26) *
в данном случае проще вычислять значение прямо при вводе.

А каким образом?

Сегодня продолжал разбираться с прерыванием - так и не возникает. Программа чучуть изменилась. Текст теперь вот такой:
http://files.mail.ru/0LTSRM
В чем может быть проблема?
esaulenka
Цитата
При нажатии клавиши может отобразиться много цифр на индикаторе.

Ну так надо ждать, пока пользователь отпустит клавишу. Или, по-хорошему, вводить алгоритм подавления дребезга.

И в третий раз говорю, что в той строчке есть ошибка.


Цитата
А каким образом?

Сейчас ввод цифр производится в основном цикле, а математика - в прерывании. Может, не стоит их разносить?

Цитата
Программа чучуть изменилась.

Надо не чучуть. По-хорошему, это надо переделывать, ни разу не пользуясь буфером обмена.
Если в программе попадается десяток одинаковых (или почти одинаковых) кусков, это большой повод задуматься, как вынести повторяющийся код в отдельную функцию.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.