реклама на сайте
подробности

 
 
2 страниц V   1 2 >  
Reply to this topicStart new topic
> Светодиодиодна индикация на PIC18F6722, Как правильно написать программу?
Andbiz
сообщение Aug 16 2012, 16:40
Сообщение #1


Местный
***

Группа: Свой
Сообщений: 447
Регистрация: 16-11-08
Из: Украина, Донецк
Пользователь №: 41 684



Здравствуйте, уважаемые форумчане!
Пробую освоить микроконтроллер 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 сигнал подается на ждущий таймер и индикаторы. Данный вывод у меня будет задействован в индикации и будет периодически менять свое значение, т.е. будет сбрасываться. Правильно ли я думаю? Правильно ли я написал программу? Правильно ли я указал конфигурационные биты в начале программы?
Go to the top of the page
 
+Quote Post
Andbiz
сообщение Aug 17 2012, 17:29
Сообщение #2


Местный
***

Группа: Свой
Сообщений: 447
Регистрация: 16-11-08
Из: Украина, Донецк
Пользователь №: 41 684



Программу записал в МК.
В результате получилась следующая ситуация на индикаторах:
Индикатор 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 и записывать в него любые значения. При этом этот вывод неподключен. Дешифратор работает верно, значения все записываю верно.
Уже потратил весь день на поиски причины этого сдвига. Из-за чего это может происходить?
Go to the top of the page
 
+Quote Post
Ruslan1
сообщение Aug 17 2012, 19:53
Сообщение #3


Гуру
******

Группа: Свой
Сообщений: 2 360
Регистрация: 6-03-06
Из: Кишинев
Пользователь №: 15 025



на ИР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
Go to the top of the page
 
+Quote Post
ILYAUL
сообщение Aug 17 2012, 20:14
Сообщение #4


Профессионал
*****

Группа: Свой
Сообщений: 1 940
Регистрация: 16-12-07
Из: Москва
Пользователь №: 33 339



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

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


--------------------
Закон Мерфи:

Чем тщательнее составлен проект, тем больше неразбериха, если что-то пошло не так
Go to the top of the page
 
+Quote Post
Andbiz
сообщение Aug 20 2012, 15:28
Сообщение #5


Местный
***

Группа: Свой
Сообщений: 447
Регистрация: 16-11-08
Из: Украина, Донецк
Пользователь №: 41 684



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

Цитата
на ИР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 и не использовалась функция. В остальном она как будто-бы подобна Вашей. Но Ваша все же работает. Почему не работала моя программа? Почему происходила неправильная индикация?
Go to the top of the page
 
+Quote Post
Ruslan1
сообщение Aug 20 2012, 17:20
Сообщение #6


Гуру
******

Группа: Свой
Сообщений: 2 360
Регистрация: 6-03-06
Из: Кишинев
Пользователь №: 15 025



Цитата(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
Пройдитесь пошагово по Вашей программе, посмотрите в какой момент после какой команды что происходит, что зажигается-что сбивается в индикации, сразу многое станет ясно.
Go to the top of the page
 
+Quote Post
Andbiz
сообщение Aug 20 2012, 17:52
Сообщение #7


Местный
***

Группа: Свой
Сообщений: 447
Регистрация: 16-11-08
Из: Украина, Донецк
Пользователь №: 41 684



Сигнал 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.


Я это понимаю. Хочу попытаться попробовать попытаться осуществить счет на индикаторах от нолях до произвольного значения. Здесь понадобиться обнулять значения.
Go to the top of the page
 
+Quote Post
Andbiz
сообщение Aug 21 2012, 15:40
Сообщение #8


Местный
***

Группа: Свой
Сообщений: 447
Регистрация: 16-11-08
Из: Украина, Донецк
Пользователь №: 41 684



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

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

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

Верно?
Go to the top of the page
 
+Quote Post
_Артём_
сообщение Aug 21 2012, 15:55
Сообщение #9


Гуру
******

Группа: Свой
Сообщений: 2 128
Регистрация: 21-05-06
Пользователь №: 17 322



Цитата(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 ();    //пауза
        }
    }
}
Go to the top of the page
 
+Quote Post
esaulenka
сообщение Aug 21 2012, 16:28
Сообщение #10


Профессионал
*****

Группа: Свой
Сообщений: 1 032
Регистрация: 13-03-08
Из: Маськва
Пользователь №: 35 877



Маленький совет: при написании комментариев забыть про буфер обмена. Вообще совсем.
Тогда вот это
Код
#define ADDR_HL1 0x3    // определяю адрес первого индикатора HL1 0x03, как переменную ADDR_HL1
#define ADDR_HL2 0x4    // определяю адрес второго индикатора HL2 0x04, как переменную ADDR_HL2

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

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


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


--------------------
Тут обсуждается творческий порыв, а не соответствие каким-либо стандартам ©
Go to the top of the page
 
+Quote Post
_Артём_
сообщение Aug 21 2012, 16:36
Сообщение #11


Гуру
******

Группа: Свой
Сообщений: 2 128
Регистрация: 21-05-06
Пользователь №: 17 322



Цитата(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 переменные не объявляет.
Go to the top of the page
 
+Quote Post
_Ivana
сообщение Aug 21 2012, 16:43
Сообщение #12


Местный
***

Группа: Свой
Сообщений: 352
Регистрация: 13-08-11
Из: Воронеж
Пользователь №: 66 710



Да, читая комментарии я только ещё больше путаюсь в этом коде и не сразу понял подвох даже в таком простом куске с таким комментарием sm.gif
Цитата
void delay (void) //организую подпрограмму задержки
{
unsigned int i;
for (i==0; i<6000; i++); //длительность задержки опрделяется величиной i
}
Go to the top of the page
 
+Quote Post
esaulenka
сообщение Aug 21 2012, 17:23
Сообщение #13


Профессионал
*****

Группа: Свой
Сообщений: 1 032
Регистрация: 13-03-08
Из: Маськва
Пользователь №: 35 877



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

Цитата
при работе ЦАП необходимо опорное напряжение, относительно которого будет производиться измерение напряжения.
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


--------------------
Тут обсуждается творческий порыв, а не соответствие каким-либо стандартам ©
Go to the top of the page
 
+Quote Post
Andbiz
сообщение Aug 22 2012, 17:01
Сообщение #14


Местный
***

Группа: Свой
Сообщений: 447
Регистрация: 16-11-08
Из: Украина, Донецк
Пользователь №: 41 684



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

    }
}


Каким образом лучше сделать соответствие между измеренным значением напряжения с АЦП в десятичной форме и индикаторами? Т.е. каким образом можно привязать целую часть и дробную часть числа к отдельным индикатором и выводить на нее необходимую комбинацию сегментов?
Go to the top of the page
 
+Quote Post
Ruslan1
сообщение Aug 23 2012, 08:54
Сообщение #15


Гуру
******

Группа: Свой
Сообщений: 2 360
Регистрация: 6-03-06
Из: Кишинев
Пользователь №: 15 025



Про индикацию:
самое простое для разборок- это
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
Go to the top of the page
 
+Quote Post

2 страниц V   1 2 >
Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 22nd July 2025 - 04:00
Рейтинг@Mail.ru


Страница сгенерированна за 0.0161 секунд с 7
ELECTRONIX ©2004-2016