Перейти к содержанию
    

Светодиодиодна индикация на PIC18F6722

Здравствуйте, уважаемые форумчане!

Пробую освоить микроконтроллер PIC18F6722. Программа для работы – MPLAB IDE v8.86 + компилятор С18. Программатор – MPLAB ICD3. Есть плата, на которой уже установлен данный микроконтроллер и есть светодиоды, которые можно «позажигать», чтобы немного разобраться в работе данного МК.

 

Вот схемы платы:

post-41684-1345134946_thumb.jpg

post-41684-1345135121_thumb.jpg

post-41684-1345135130_thumb.jpg

 

Микроконтроллер показан на третьей схеме.

Кварц стоит внешний – на 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 сигнал подается на ждущий таймер и индикаторы. Данный вывод у меня будет задействован в индикации и будет периодически менять свое значение, т.е. будет сбрасываться. Правильно ли я думаю? Правильно ли я написал программу? Правильно ли я указал конфигурационные биты в начале программы?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Программу записал в МК.

В результате получилась следующая ситуация на индикаторах:

Индикатор HL1 – 2

Индикатор HL2 – 3

Индикатор HL3 – 4

Индикатор HL4 – 5

Индикатор HL5 – 6

Индикатор HL6 – 7

Индикатор HL7 – 8

Индикатор HL8 – 8

 

post-41684-1345224553_thumb.jpg

 

Получилась ситуация, что все разряды сдвинулись на одно значение в сторону. Схема верная – прозвонил, ошибок в ней нет.

Дешифратор работает верно.

Сигналы 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 и записывать в него любые значения. При этом этот вывод неподключен. Дешифратор работает верно, значения все записываю верно.

Уже потратил весь день на поиски причины этого сдвига. Из-за чего это может происходить?

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

на ИР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.

 

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1. Коментарии должны быть полезны, а не просто набор слов.
+ слово ПОРТ в данной фразе не склоняется

Сбрасываю значения на порте D

Сбрасываю значения на ПОРТ D

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Большое спасибо за помощь!

 

на ИР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 и не использовалась функция. В остальном она как будто-бы подобна Вашей. Но Ваша все же работает. Почему не работала моя программа? Почему происходила неправильная индикация?

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Принцип такой:

1) LE на ИР22 неактивный (L)- значение на выходах не зависит от значения на входе.

2) LE на ИР22 активный (H) - значение на выходах то же самое что значение на входах

3) переход LE H->L: дальше на выходах то значение, которое было в момент этого перехода.

 

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

 

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

 

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

 

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

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

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

 

    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, это отладчик. :)

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Сигнал LE1 идет на микросхему D7 ЭКФ1533ИР22 (первая схема)

Далее сигналы с этой микросхему идут на ключи для управления внешними светодиодами:

post-41684-1345484603_thumb.jpg

 

Чудес не бывает, нужно понять почему и что. Разверните фразу "периодически появляются одно временно восьмерки на 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.

 

Я это понимаю. Хочу попытаться попробовать попытаться осуществить счет на индикаторах от нолях до произвольного значения. Здесь понадобиться обнулять значения.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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

 

#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) не подключены - подпаяться тоже проблематично.

 

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

 

Верно?

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Программа работает, сегменты изменяются.

Вопросы:

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 ();    //пауза
        }
    }
}

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Маленький совет: при написании комментариев забыть про буфер обмена. Вообще совсем.

Тогда вот это

#define ADDR_HL1 0x3    // определяю адрес первого индикатора HL1 0x03, как переменную ADDR_HL1
#define ADDR_HL2 0x4    // определяю адрес второго индикатора HL2 0x04, как переменную ADDR_HL2

пишется как

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

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

 

 

И также присоединяюсь к предложению почитать книжки. Классика жанра - Керниган-Ричи, но можно и полистать более "упрощённый" вариант с упором в микроконтроллеры.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Маленький совет: при написании комментариев забыть про буфер обмена. Вообще совсем.

Тогда вот это

#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 переменные не объявляет.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Да, читая комментарии я только ещё больше путаюсь в этом коде и не сразу понял подвох даже в таком простом куске с таким комментарием :)

void delay (void) //организую подпрограмму задержки

{

unsigned int i;

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

}

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

и о ЦАПах, которые на самом деле очень даже АЦП

 

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

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

Ну и ладно. Эти входы являются опциональными, о чём написано на второй же станице даташита в разделе АЦП. По умолчанию опорными являются AVDD и AVSS.

Точности в сотую вольта, с таким подходом добиться будет сложно (какая там у Вас точность опорного? 2%? а мерять-то хочется с точностью в 10 раз больше!) , ну да не с профессиональными же вольтметрами конкурировать :)

 

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

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

 

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

Вопрос привычки. При наличии нормальной паяльной станции (надо разводить на деньги начальство! хотя бы на 100$, потом очень пригодится) работы на пару минут вместе с подбором тоненькой проволочки, нагревом паяльника и протиркой результатов спиртом :)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

_Артём_

Спасибо, понял – более компактный код можно достичь организацией матрицы, ее заполнением, а затем выводом элементов матрицы поочередно на индикаторы организацией цикла. #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 В и это значение нужно будет отобразить на семисегментном индикаторе. При уменьшении напряжения значение на индикаторе должно соответственно уменьшаться.

 

post-41684-1345654368_thumb.jpg

 

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)
    {
    
    Тут будет программа

    }
}

 

Каким образом лучше сделать соответствие между измеренным значением напряжения с АЦП в десятичной форме и индикаторами? Т.е. каким образом можно привязать целую часть и дробную часть числа к отдельным индикатором и выводить на нее необходимую комбинацию сегментов?

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Про индикацию:

самое простое для разборок- это

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 одновременно с символом могут точку светить в этом же разряде, но это уже следующий этап игры :)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
К сожалению, ваш контент содержит запрещённые слова. Пожалуйста, отредактируйте контент, чтобы удалить выделенные ниже слова.
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...