Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Опять двадцать пять
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему
Василий_Безкатегорийный
Прошу не пинать сильно. Искал сабж по форуму, читал, но чегой-то недопонял. Есть Atmega16, компилятор IAR. К МК подключены 2 семисегментных индикатора (какие -то старенькие советские индикаторы). Схема подключения такая- на сегменты идёт сигнал с порта С (PC0-PC7)через резисторы 82 Ом, общие провода индикаторов подсоединены подсоединены к порту D (PD0 и PD2). Чтобы горели сразу два индикатора с неизменяющимся двухзначным числом написал такую программу.
---------------------------------------------------------------------------------------------------------


#include <ioavr.h>
#include <iom16.h>
#include <intrinsics.h>
int main( void )
{
int digit[10];

DDRC=0xFF; //настраиваем порт С на вывод
digit[0]=0x3F;
digit[1]=0x06;
digit[2]=0x5B;
digit[3]=0x4F; // цифры от 0 до 9
digit[4]=0x66;
digit[5]=0x6D;
digit[6]=0x7D;
digit[7]=0x07;
digit[8]=0x7F;
digit[9]=0x6F;
while(1) // бесконечный цикл
{
DDRD=0x04; // подаём единицу на PD2 -инициализируем один индикатор
PORTC =digit[1]; // подаём код цифры
__delay_cycles(2500); // задержка
DDRD=0x01; // подаём единицу на PD0-инициализируем второй индикатор
PORTC =digit[0]; // подаём код другой цифры
__delay_cycles(2500); // задержка
}
}
---------------------------------------------------------------------------------------------
А вот чтобы допустим 2 индикатора динамически отображали счёт до 99, например, не получается ;-(
Программа такая:
-------------------------------------------------------------------------
#include <ioavr.h>
#include <iom16.h>
#include <intrinsics.h>
int main( void )
{
int i;
int j;
int digit[10];
DDRC=0xFF;

digit[0]=0x3F;
digit[1]=0x06;
digit[2]=0x5B;
digit[3]=0x4F;
digit[4]=0x66;
digit[5]=0x6D;
digit[6]=0x7D;
digit[7]=0x07;
digit[8]=0x7F;
digit[9]=0x6F;
while(1)
{
for (j=0;j<10;j=j+1)
for (i=0;i<10;i=i+1)
{DDRD=0x04;
PORTC =digit[j];
__delay_cycles(90000);

DDRD=0x01;
PORTC =digit[i];
__delay_cycles(5000000);
}
}
}
=================================================
Пните пожалуйста, что и как писать чтобы считала до 99 в бесконечном цикле и при этом 2 индикатора горели постоянно. На Си это первые опыты для микроконтроллеров, с начала начал осваивать ассемблер.
rezident
У вас не совсем верный алгоритм. Счет числа от 0 до 99 и управление дин. индикацией в общем случае это асинхронные процессы. Точнее даже тут три асинхронных процесса:
- счет,
- вывод на индикацию,
- динамическая индикация.
Синхронизируются асинхронные процессы обычно с помощью буферов.
Считать вы можете с любой требуемой вам скоростью.
Выводить число на индикацию чаще 3 раз в секунду не имеет смысла, т.к. во-первых у человеческого глаза инерционность порядка 0,1с. Во-вторых, мозг человека информацию меняющуюся чаще 3 раз в сек просто не успевает адекватно обрабатывать.
А вот динамическую индикацию нужно обрабатывать не реже 25-30 раз в секунду, чтобы за счет инерционности зрения не видеть мерцания индикаторов.
Так что вам нужно реализовать как минимум два конечных автомата.
Один из них будет переключать символы (катоды в вашем случае) индикатора (через порт D) и выводить из своего собственного буфера значение для соответствующего индикатора в порт C. Эту процедуру рекомендую вызывать из регулярного прерывания, например, от правильным образом настроенного таймера.
Второй КА это просто функция, которая подготавливает данные для отображения на 7-сегментном индикаторе. Проще всего реализовать перевод символов/чисел в значение для индикатора на основе таблицы.
Вместо использования функции __delay_cycles() рекомендую в использовать функцию __delay_ms(), если такая есть. Хотя я лично предпочитаю использовать некое подобие RTC в виде простого счетчика миллисекунд. В том же прерывании от таймера, в котором "крутится" динамическая индикация, я инкрементирую 16-ти или 32-х разрядную переменную. Причем инкремент не на 1, а на число соответствующее периоду таймера в мс. Допустим у меня таймер работает с частотой перезагрузки 100 раз в сек. Следовательно я инкрементирую каждый раз счетчик мс на 10. Чтобы в основном цикле организовать задержку для вывода на индикацию в 0,3 сек достаточно вызвать delay_ms(300). Сама функция delay_ms совершенно простая. Типа такой.
Код
extern volatile unsigned int TimeCntrMS;
void delay_ms(unsigned int value)
{ unsigned int tmp=TimeCntrMS; //запомним текущее значение счетчика мс
while((TimeCntrMS-tmp)<value); //ждем пока счетчик мс не превысит свое запомненное состояние (tmp) плюс значение задержки (value)
}
Василий_Безкатегорийный
Спасибо за подробный ответ!
Цитата
Синхронизируются асинхронные процессы обычно с помощью буферов.

Вы имеете ввиду микросхемы буферов?

А вот если идёт счёт на двух индикаторах до 99 секунд?
Мне всё равно надо использовать один таймер?
Т.е таймер настраивается на прерывания через секунду
в нём же вырабатывается частота для динамической индикации 25-30 раз в секунду
в нём же - частота для вывода цифр - 2-3 раза в секунду.
Я правильно понял?
Или тут надо 2 таймера использовать(возможно это или нет, я не знаю sad.gif )?
upc2
В процедуре прерывания устанавливаются определенные флаги
В основном цикле программы по этим флагам вызываются соответсвующие
процедуры. Вот пример формирования мсек, секунд,минут и часов.
void interrupt isr(void){
TMR1IF=0;
}
if(T0IF) //  
{
TMR0=100; //
T0IF=0; //
TIME++; //
if(TIME > 4)
{
TIME=0;
T_MC++;
}
if(T_MC > 999)
{
T_MC=0;
T_SEC++;
}
if(T_SEC > 59)
{
T_SEC=0;
T_MIN++;
}
if(T_MIN > 59)
{
T_MIN=0;
T_HOUR++;
}
if(T_HOUR > 23)
{
T_HOUR=0;
}
}
}
rezident
Цитата(Василий_Безкатегорийный @ Jun 20 2007, 18:57) *
Вы имеете ввиду микросхемы буферов?

Конечно нет. Я имел в виду программный буфер в ОЗУ, контролируемый (программным) счетчиком и семафором.
Цитата(Василий_Безкатегорийный @ Jun 20 2007, 18:57) *
А вот если идёт счёт на двух индикаторах до 99 секунд?
Мне всё равно надо использовать один таймер?
Т.е таймер настраивается на прерывания через секунду
в нём же вырабатывается частота для динамической индикации 25-30 раз в секунду
в нём же - частота для вывода цифр - 2-3 раза в секунду.
Я правильно понял?

Опять не правильно. Таймер нужен один и настраивается он на минимальный необходимый период. Раз у вас наивысшая частота 30Гц, то таймер должен выдавать прерывания и работать с периодом перезагрузки примерно в 1/(30*2)=16мс (2-число разрядов индикатора). Только это число не кратно целым долям секунды и секундный интервал считать не очень удобно. Поэтому возьмите период 20 мс (25Гц для дин.индикации) или даже 10мс. 10мс кратно периоду сетевого напряжения и целым долям секунды.
На каждый период таймера вызывается процедура для переключения разрядов и для вывода данных отображения текущего разряда. Тут же счетчик разрядов инкрементируется.
При выводе изображения вы должны делать паузу на 32-35 периодов таймера. Это будут как раз примерно 3Гц (задержка 320-350мс).
Для отсчета секунды вы будете использовать паузу в 100 последовательных периодов таймера.
Василий_Безкатегорийный
С Вашей огромной помощью, с помощью сайта pcports(точка)ru (сообственно смодифицировал код с этого прекрасного сайта)и radiokot(точка)ru скомпилировал программу счёта до 99 секунд на двух индикаторах. Сделана она пока не так как советовал -rezident-, т.е задержка вывода на индикацию и переключения катодов осуществляется задержками в цикле, а не, например, процедурой delay_ms. Приведу исходный код с объяснениями, может кому-нибудь понадобится. Может есть неточности или неграммотный код, не обессудьте.
// катоды индикаторов подключены к PD0 и PD2, сегменты к PC0..PC7
#include "iom16.h"
#include "inavr.h"

short unsigned int Number = 0;
unsigned char Dig[10];
// В этих переменных хранятся цифры, которые нужно отобразить
char Disp6, Disp7;

// Функция выделяет цифры из трехзначного числа Number (вычисляет целое количество десяток в числе и присваивают их переменной Num2, единицы в остатке присваиваются переменной Num3)
void Display (short unsigned int Number)
{
unsigned char Num1, Num2, Num3;
Num2=0;
while (Number >= 10)
{
Number -= 10;
Num2++;
}
Num3 = Number;
Disp6 = Dig[Num2];
Disp7 = Dig[Num3];
}

void i1_init() //Инициализация портов ввода/вывода
{
DDRC = 0xFF;
DDRD |= (1 << 0) |(1 << 2);
}

void timer0_init()
{ // в коде настройки прерывания на секунду
OCR1AH =0x98; //в регистре OCR1A записываем количество тактов, которое отсчитает счётчик за 1 с. OCR1AL =0x96;// рассч
TCCR1B |= (1 << 3);//включаем режим CTC (описан в даташите в mode of operations для таймера1)
TCCR1B |= (1 << 2);//прескалер настроен на деление частоты на 256
TIMSK = (1 << 4);// настройка работы таймера по сравнению TCNT1 и OCR1A
}

void Dig_init()
{
Dig[0] = 0x3F;//0
Dig[1] = 0x06;//1
Dig[2] = 0x5B;//2
Dig[3] = 0x4F;//3
Dig[4] = 0x66;//4
Dig[5] = 0x6D;//5
Dig[6] = 0x7D;//6
Dig[7] = 0x07;//7
Dig[8] = 0x7F;//8
Dig[9] = 0x6F;//9
}

void main()
{
unsigned char j, k = 0;
Dig_init();
Display(0);
i1_init();
timer0_init();
SREG |= (1 << 7);
while(1)
{
for (j = 0; j <= 50; j++){} // Задержка для отображения цифры
(k == 1) ? k = 0 : k++;
for (j = 0; j<=30; j++){} // Задержка для переключения катода
switch (k)
{
case 0: PORTD=0x04;//Единицы
PORTC = Disp7;
break;
case 1: PORTD=0x01; // Десятки
PORTC = Disp6;

}
}
}

#pragma vector = TIMER1_COMPA_vect // процедура обработки прерывания таймера 1
__interrupt void Indic_change()
{
if (Number < 99)
Number++;// Увеличение отображаемого числа.
else
Number = 0;
Display(Number);

}
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.