|
Светодиодиодна индикация на PIC18F6722, Как правильно написать программу? |
|
|
|
Aug 16 2012, 16:40
|
Местный
  
Группа: Свой
Сообщений: 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 сигнал подается на ждущий таймер и индикаторы. Данный вывод у меня будет задействован в индикации и будет периодически менять свое значение, т.е. будет сбрасываться. Правильно ли я думаю? Правильно ли я написал программу? Правильно ли я указал конфигурационные биты в начале программы?
|
|
|
|
2 страниц
1 2 >
|
 |
Ответов
(1 - 14)
|
Aug 17 2012, 17:29
|
Местный
  
Группа: Свой
Сообщений: 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 и записывать в него любые значения. При этом этот вывод неподключен. Дешифратор работает верно, значения все записываю верно. Уже потратил весь день на поиски причины этого сдвига. Из-за чего это может происходить?
|
|
|
|
|
Aug 17 2012, 19:53
|
Гуру
     
Группа: Свой
Сообщений: 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. Основной штамп на первом листе Вы благоразумно затерли, иначе я бы оперировал конкретным именем разработчика
|
|
|
|
|
Aug 17 2012, 20:14
|

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

|
Цитата 1. Коментарии должны быть полезны, а не просто набор слов. + слово ПОРТ в данной фразе не склоняется Цитата Сбрасываю значения на порте D Сбрасываю значения на ПОРТ D
--------------------
Закон Мерфи:
Чем тщательнее составлен проект, тем больше неразбериха, если что-то пошло не так
|
|
|
|
|
Aug 20 2012, 15:28
|
Местный
  
Группа: Свой
Сообщений: 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 и не использовалась функция. В остальном она как будто-бы подобна Вашей. Но Ваша все же работает. Почему не работала моя программа? Почему происходила неправильная индикация?
|
|
|
|
|
Aug 20 2012, 17:20
|
Гуру
     
Группа: Свой
Сообщений: 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, это отладчик.  Пройдитесь пошагово по Вашей программе, посмотрите в какой момент после какой команды что происходит, что зажигается-что сбивается в индикации, сразу многое станет ясно.
|
|
|
|
|
Aug 20 2012, 17:52
|
Местный
  
Группа: Свой
Сообщений: 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. Я это понимаю. Хочу попытаться попробовать попытаться осуществить счет на индикаторах от нолях до произвольного значения. Здесь понадобиться обнулять значения.
|
|
|
|
|
Aug 21 2012, 15:40
|
Местный
  
Группа: Свой
Сообщений: 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) не подключены - подпаяться тоже проблематично. Как я понимаю, организовать ЦАП без использования этих входов невозможно, т.е. создание на этой плате устройства с применением ЦАП (измерение напряжения). Верно?
|
|
|
|
|
Aug 21 2012, 15:55
|
Гуру
     
Группа: Свой
Сообщений: 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 (); //пауза } } }
|
|
|
|
|
Aug 21 2012, 16:28
|

Профессионал
    
Группа: Свой
Сообщений: 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 // второй индикатор Пожалейте глаза того, кто это читать будет - полезной информации в первом примере куда меньше половины. И также присоединяюсь к предложению почитать книжки. Классика жанра - Керниган-Ричи, но можно и полистать более "упрощённый" вариант с упором в микроконтроллеры.
--------------------
Тут обсуждается творческий порыв, а не соответствие каким-либо стандартам ©
|
|
|
|
|
Aug 21 2012, 16:36
|
Гуру
     
Группа: Свой
Сообщений: 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 переменные не объявляет.
|
|
|
|
|
Aug 21 2012, 16:43
|
Местный
  
Группа: Свой
Сообщений: 352
Регистрация: 13-08-11
Из: Воронеж
Пользователь №: 66 710

|
Да, читая комментарии я только ещё больше путаюсь в этом коде и не сразу понял подвох даже в таком простом куске с таким комментарием  Цитата void delay (void) //организую подпрограмму задержки { unsigned int i; for (i==0; i<6000; i++); //длительность задержки опрделяется величиной i }
|
|
|
|
|
Aug 21 2012, 17:23
|

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

|
и о ЦАПах, которые на самом деле очень даже АЦП Цитата при работе ЦАП необходимо опорное напряжение, относительно которого будет производиться измерение напряжения. VREF+ (21 ножка МК) - не задействована и не подключена. Микросхема в корпусе 64-Pin TQFP. Шаг ножек очень мелкий, припаять провод проблематично. VREF- (22 ножка МК) задействована в качестве выхода под дешифратор D4. Ну и ладно. Эти входы являются опциональными, о чём написано на второй же станице даташита в разделе АЦП. По умолчанию опорными являются AVDD и AVSS. Точности в сотую вольта, с таким подходом добиться будет сложно (какая там у Вас точность опорного? 2%? а мерять-то хочется с точностью в 10 раз больше!) , ну да не с профессиональными же вольтметрами конкурировать  Цитата Вывода AVDD и AVSS (ножки 19 и 20) не подключены - Ну это явная ошибка разработчика. Если я правильно помню микрочиповские даташиты (крайний раз работал с пиками в студенчестве), где-то была специальная схема "какие ноги минимально необходимо соединить". В их число входят ВСЕ ноги питания, т.е. в данном случае по 4 ноги VSS / VDD, а также AVSS/AVDD. Цитата подпаяться тоже проблематично. Вопрос привычки. При наличии нормальной паяльной станции (надо разводить на деньги начальство! хотя бы на 100$, потом очень пригодится) работы на пару минут вместе с подбором тоненькой проволочки, нагревом паяльника и протиркой результатов спиртом
--------------------
Тут обсуждается творческий порыв, а не соответствие каким-либо стандартам ©
|
|
|
|
|
Aug 22 2012, 17:01
|
Местный
  
Группа: Свой
Сообщений: 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) { Тут будет программа
} } Каким образом лучше сделать соответствие между измеренным значением напряжения с АЦП в десятичной форме и индикаторами? Т.е. каким образом можно привязать целую часть и дробную часть числа к отдельным индикатором и выводить на нее необходимую комбинацию сегментов?
|
|
|
|
|
Aug 23 2012, 08:54
|
Гуру
     
Группа: Свой
Сообщений: 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 одновременно с символом могут точку светить в этом же разряде, но это уже следующий этап игры
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|