Страница 317 из 421
Re: CodeVision AVR в вопросах и ответах
Добавлено: Вт авг 05, 2014 16:32:50
WiseLord
DataLife писал(а):Так вот, "фантомы" из правого разряда переходят на соседний левый!
Я уже выше писал про возможную "инерционность" светоидодов. Колько скоро недавно выяснилось, что для управления разрядами используются ключи на npn-транзисторах.. попробуйте резисторы на базы таких транзисторов уменьшить раз в 10 (до приблизительно килоома). Просто параллельно напаять, как вариант. Вдруг именно где-то в этих цепях появляется задержка?
У меня-то и на в 5 раз большей частоте работало без "засветок", но я без транзисторных ключей делал.
Re: CodeVision AVR в вопросах и ответах
Добавлено: Вт авг 05, 2014 17:12:16
zero648
DataLife писал(а):Да, там скорее всего опечатка. Там выбраны резисторы 56к и 15к (56/15 = 3,733).
Как это опечатка?, все праливно, коэф. с резиками 56к и 15к будет 4.73, а формула така: (56+15)/15=4.73
Re: CodeVision AVR в вопросах и ответах
Добавлено: Вт авг 05, 2014 17:36:48
DataLife
WiseLord, с кодом, который ниже, индикация работает шикарно, на частоте счётчика таймера2 в 1Мгц (делитель на 8 ). И всё на тех же транзисторах и резисторах.
Тут именно откуда-то берутся "фантомы" соседних разрядов. Браться им там неоткуда аппаратно, только программно. А разряды перечисляются операторами if...
zero648, прошу прощения, был не прав по поводу расчёта коэффициента, но с подобранными резисторами верно ...
Re: CodeVision AVR в вопросах и ответах
Добавлено: Вт авг 05, 2014 17:40:00
ARV
DataLife писал(а):ARV, стало кое-что поясняться. Сделал обычный "счётчик": на индикатор выводятся каждые две секунды i++;
Постараюсь доходчиво объяснить. Имеем такую картинку: четыре разряда, например выводится значение 2015: [2][0][1][5]. Так вот, "фантомы" из правого разряда переходят на соседний левый! На двоечке силуэт ноля, на ноле (хоть и не видно) есть единичка, на единице - пятёрка. Когда "счётчик" тикает - это хорошо заметно.
Получается косяк теперь скорее ряде операторов
if, мне так видится...

чудны дела твои, господи
а на 5-ке видно след от первой 2-ки?
смотрю я ваш код и не понимаю, откуда такой эффект. ну не должно бы вроде быть его... пока не понимаю вообще...
P.S. кстати, вы ставите точку и гасите левый ноль в обработчике прерываний... а почему бы вам не делать это сразу в функции
data_convert ?
Re: CodeVision AVR в вопросах и ответах
Добавлено: Вт авг 05, 2014 17:57:28
DataLife
ARV, чуднее некуда ...
Да, на последнем разряде "фантом" первого. Всё по кругу.
По поводу гашения ноля и точки - пока что хочу заставить работать код так (или даже без гашения ноля и точки), после оптимизировать ...
Re: CodeVision AVR в вопросах и ответах
Добавлено: Вт авг 05, 2014 18:20:03
zero648
DataLife, у Вас фантомы крутятся по кругу вроде шлейфа, возможно где-то в коде происходит накладка, а именно, сначала включаете разряд, а потом выводите в порт сегменты следующего по очереди разряда, или наоборот, выводите в порт сегменты, а потом переключаете разряд. Чтобы не было накладок, выключайте разряды и гасите сегменты, а потом уже выводите/включайте сегменты и разряд или наоборот.
Re: CodeVision AVR в вопросах и ответах
Добавлено: Вт авг 05, 2014 18:51:27
DataLife
zero648, дык всё так и есть ...
Код: Выделить всё
void ind_update (void)
{
static unsigned char count = 0;
PORTD=0x00; // гасим сегменты
PORTB &= ~((1<<0)|(1<<1)|(1<<2)|(1<<3)); // гасим все разряды
PORTD = adc_data[count]; // выводим в порт код цифры (сегменты)
if (count == 0) PORTB |= (1<<0); // определяем какой разряд должен гореть (ИМХО, тут копать нужно)
if (count == 1) PORTB |= (1<<1);
if (count == 2) PORTB |= (1<<2);
if (count == 3) PORTB |= (1<<3);
count++; // включаем следующий разряд
if (count == 4) count = 0;
}
Может кого наталкнёт на мысль, но я попробовал строчку, где гасятся разряды, перекинуть в конец функции и "фантомы" исчезли, но сегменты горят в пол-накала. PORTD=0x00; убрал из кода.
Re: CodeVision AVR в вопросах и ответах
Добавлено: Вт авг 05, 2014 20:58:18
Pink-Pank
Код: Выделить всё
void ind_update (void)
{
static unsigned char count = 0;
PORTB=0x00; // гасим сегменты
PORTD = adc_data[count]; // выводим в порт код цифры (сегменты)
if (count == 0) PORTB = (1<<0); // определяем какой разряд должен гореть (ИМХО, тут копать нужно)
if (count == 1) PORTB = (1<<1);
if (count == 2) PORTB = (1<<2);
if (count == 3) PORTB = (1<<3);
count++; // включаем следующий разряд
if (count == 4) count = 0;
}
Re: CodeVision AVR в вопросах и ответах
Добавлено: Вт авг 05, 2014 21:01:39
DataLife
Pink-Pank, было бы всё так просто. Пробовал и так. Не то ...

Re: CodeVision AVR в вопросах и ответах
Добавлено: Вт авг 05, 2014 21:05:21
Pink-Pank
прямо точно так пробовали? И что не то?
Re: CodeVision AVR в вопросах и ответах
Добавлено: Вт авг 05, 2014 21:09:25
DataLife
Прям-прям точно так ...
Спойлер
Код: Выделить всё
#include <mega8.h>
#include <delay.h>
//Объявляем переменные
//------------------0-----1-----2-----3-----4-----5-----6-----7-----8------9----dp
char SEGMENTE[] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F, 0x80};
volatile char adc_data[4];
volatile unsigned int ADC_volt;
/** ФУНКЦИЯ ПРЕОБРАЗОВАНИЯ **/
void data_convert(unsigned int value)
{
unsigned char tmp; // переменная для временного хранения данных преобразования
tmp = value / 1000;
adc_data[0] = SEGMENTE[tmp];
tmp = value % 1000 / 100;
adc_data[1] = SEGMENTE[tmp];
tmp = value % 100 / 10;
adc_data[2] = SEGMENTE[tmp];
tmp = value % 10;
adc_data[3] = SEGMENTE[tmp];
}
void ind_update (void)
{
static unsigned char count = 0;
PORTB=0x00; // гасим сегменты, для перестраховки
PORTD = adc_data[count]; // выводим в порт код цифры (сегменты)
if (count == 0) PORTB |= (1<<0); // перечисляем выводимые разряды, не выводим "ноль" в первом разряде
if (count == 1) PORTB |= (1<<1); //включаем точку во втором разряде
if (count == 2) PORTB |= (1<<2);
if (count == 3) PORTB |= (1<<3);
count++; // включаем следующий разряд
if (count == 4) count = 0;
}
void ADC_result(void)
{
unsigned int adc_tmp;
ADCSRA |= 0x40; //начинаем измерение
while((ADCSRA & 0x10)==0); //Ждём флаг окончания измерения
ADCSRA |=0x10;
adc_tmp=ADCW;
ADC_volt=(float)adc_tmp*4645/10000; // не задумываемся над вычислениями
}
// Обработчик прерывания по переполнению таймера2
interrupt [TIM2_OVF] void timer2_ovf_isr(void)
{
ind_update();
}
void main(void)
{
PORTB=0x00;
DDRB|=(1<<0)|(1<<1)|(1<<2)|(1<<3); // настройка на выход. 4 разряда
PORTD=0x00;
DDRD=0xFF; // настройка на выход всего порта
ADCSRA = 0x86; //0b10000110 - предделитель на 64, прерывания запрещены, ADC включён
ADMUX = 0x40; //0b01000000 - AVCC , ACD0, ADLAR off
TIMSK |= (1 << TOIE2); // разрешение прерывания по таймеру2
TCCR2 |= (1 << CS21); // предделитель таймера2 на 8
#asm ("sei") // глобально разрешаем прерывания.
while (1)
{
ADC_result();
data_convert(ADC_volt);
delay_ms(500);
}
}
Всё те же фантомы на соседних разрядах не дают мне покоя ...
Видны "фантомы":
Дабы не говорили, что возможна проблема в железе, вот другая программа, где нет засветки фантомами и прочей гадостью (первый разряд "0" потушен не правильно, оттого немного виден, это не "фантом")

Re: CodeVision AVR в вопросах и ответах
Добавлено: Вт авг 05, 2014 21:22:47
Pink-Pank
Могу предположить следующее:
1. Обработчик таймера очень шустрый.
2. Преобразование результата в десятичное очень медленное.
3. Во время вычисления и записи нового индицируемого значения обработчик от таймера продолжает брать результаты с той же области памяти и выводить их на экран.
4. Отсюда вполне вероятен случай, когда обработчик может вывести часть старого результата преобразования и часть нового.
5. Предлагаю создать два массива для вывода. Один - старое значение выводит таймер. Другой - новое записываемое. После записи они меняются назначением.
6. Не знаю точно про Ваши фантомы - не видел (хотя бы видео сняли что ли на телефон и выложили сюда). Но уверен, что такая ошибка у Вас имеет место быть, хотя ее проявление будет редким (и обычно в младшем разряде), но все же иногда заметным.
7. Лучше организовать небольшое время свечения индикатора, затем - небольшое время в выключенном состоянии (Dead time). Затем обработка следующего разряда. Это для того, что светодиоды не выключаются мгновенно, а продолжают гореть какое-то время после выключения (да и выключение транзистора не моментальное), а Вы не дожидаясь полного погашения тут же вносите новое значение в следующий разряд (и, как следствие, частично в предыдущий) - отсюда и фантомы. Сделайте еще одно прерывание по совпадению где-то в районе 3/4 максимального значения счетчика и в этом прерывании гасите все разряды. Это будет Вам Dead tiime.
Re: CodeVision AVR в вопросах и ответах
Добавлено: Ср авг 06, 2014 00:12:20
Rtmip
Еще вчера погонял этот код в протеусе, сразу постеснялся высказывать свои соображения...
Мне кажется суть в том, что пока обрабатывается функция
Код: Выделить всё
/** ФУНКЦИЯ ПРЕОБРАЗОВАНИЯ **/
void data_convert(unsigned int value)
успевает срабатывать таймер и почти после каждой строчки
идет прерывание.
Как временная мера, запретить прерывание во время работы функции преобразования и посмотреть что будет?
P.S. если сказал глупость, прошу прощения

Re: CodeVision AVR в вопросах и ответах
Добавлено: Ср авг 06, 2014 07:30:53
Pink-Pank
Если запретить прерывания, то индикатор на этот период потухнет. Будут моргания табло.
Re: CodeVision AVR в вопросах и ответах
Добавлено: Ср авг 06, 2014 07:45:31
Аlex
Pink-Pank писал(а):Код: Выделить всё
void ind_update (void)
{
static unsigned char count = 0;
PORTB=0x00; // гасим сегменты
PORTD = adc_data[count]; // выводим в порт код цифры (сегменты)
if (count == 0) PORTB = (1<<0); // определяем какой разряд должен гореть (ИМХО, тут копать нужно)
if (count == 1) PORTB = (1<<1);
if (count == 2) PORTB = (1<<2);
if (count == 3) PORTB = (1<<3);
count++; // включаем следующий разряд
if (count == 4) count = 0;
}
Тогда уж так:
Код: Выделить всё
void ind_update (void)
{
static unsigned char count = 0;
PORTB=0x00; // гасим сегменты
if (count == 0) PORTB = (1<<0); // определяем какой разряд должен гореть (ИМХО, тут копать нужно)
if (count == 1) PORTB = (1<<1);
if (count == 2) PORTB = (1<<2);
if (count == 3) PORTB = (1<<3);
PORTD = adc_data[count]; // выводим в порт код цифры (сегменты)
count++; // включаем следующий разряд
if (count == 4) count = 0;
В Вашем случае,
PORTB=0x00; - бесполезная операция + информация на сегментах отображается ещё до выбора следующего индикатора, а это - прямой путь к засветке.
Re: CodeVision AVR в вопросах и ответах
Добавлено: Ср авг 06, 2014 07:58:29
Pink-Pank
Можно кратко?
Не соглашусь 
Re: CodeVision AVR в вопросах и ответах
Добавлено: Ср авг 06, 2014 08:00:54
ARV
Аlex писал(а):Тогда уж так:
Код: Выделить всё
void ind_update (void)
{
static unsigned char count = 0;
PORTB=0x00; // гасим сегменты
if (count == 0) PORTB = (1<<0); // определяем какой разряд должен гореть (ИМХО, тут копать нужно)
if (count == 1) PORTB = (1<<1);
if (count == 2) PORTB = (1<<2);
if (count == 3) PORTB = (1<<3);
PORTD = adc_data[count]; // выводим в порт код цифры (сегменты)
count++; // включаем следующий разряд
if (count == 4) count = 0;
не соглашусь. рассмотрим работу функции:
1. вход в функцию: индикатор отображает 0-й разряд, count = 1.
2. PORTB = 0; - гасим индикатор
3. цепочкой if-ов ВКЛЮЧАЕМ СЛЕДУЮЩИЙ разряд, т.е. зажигаем в 1-ом разряде то, что выводилось ранее в PORTD, т.е. инфу для 0-го разряда!!! это и есть подсветка соседнего знакоместа
4. PORTD = adc_data[count]; - обновляем сегменты, т.е. теперь в 1-ом знакоместе светится то, что и должно
5. вычисляем count для следующего знакоместа.
таким образом в предлагаемом алгоритме ГАРАНТИРУЕТСЯ подсветка сегментов!!!
безусловно правильный алгоритм:
1. гасим ВСЕ РАЗРЯДЫ
2. обновляем СЕГМЕНТЫ
3. включаем СЛЕДУЮБЩИЙ разряд
Re: CodeVision AVR в вопросах и ответах
Добавлено: Ср авг 06, 2014 08:01:11
Аlex
Тьфу, блин. Прошу прощения за мою невнимательность, порты разные
Да, всё верно, всё должно работать.
Re: CodeVision AVR в вопросах и ответах
Добавлено: Ср авг 06, 2014 08:07:44
Аlex
Мне показалось, что в этом куске кода :
Код: Выделить всё
PORTB=0x00; // гасим сегменты
PORTD = adc_data[count]; // выводим в порт код цифры (сегменты)
порт один и тот-же, т.к. в комментарии написано "гасим сегменты"

В таком случае, вот вариант:
Код: Выделить всё
void ind_update (void)
{
static unsigned char count = 0;
PORTD=0x00; // гасим сегменты
if (count == 0) PORTB = (1<<0); // определяем какой разряд должен гореть (ИМХО, тут копать нужно)
if (count == 1) PORTB = (1<<1);
if (count == 2) PORTB = (1<<2);
if (count == 3) PORTB = (1<<3);
PORTD = adc_data[count]; // выводим в порт код цифры (сегменты)
count++; // включаем следующий разряд
if (count == 4) count = 0;
Погасили сегменты, включили общий, зажгли сегменты.
ЗЫ: Вообще, как уже говорилось выше, правильным вариантом будет - использование своего отдельного буфера для индикации.
Re: CodeVision AVR в вопросах и ответах
Добавлено: Ср авг 06, 2014 08:11:11
ARV
почему бы не сделать так:
Код: Выделить всё
static char count = 0;
// гасим все
PORTB=0;
// вычисляем следующую позицию
if(++count >= 4) count = 0;
// обновляем сегменты
PORTD = adc_data[count];
// включаем индикацию
PORTB = 1 << count;
во-первых, кратко и понятно записано
во-вторых, между гашением и зажиганием проходит довольно много времени на вычисления, что дает гарантию отсутствия "эффектов" из-за "медленного" погасания светодиодов (запирания транзисторов - если применяли что-то типа П213

)