пʼятницю, 16 січня 2015 р.

Апаратний ШІМ мікроконтролера Atmega8 + PWM+LCD







#include <mega8.h>
#include <delay.h>
#include <stdio.h>

#asm
   .equ __lcd_port=0x12 ;PORTD
#endasm
#include <lcd.h>

#define ADC_VREF_TYPE 0x40// Опорна напруга АЦП з ножки AVCC
//TCCR1B
#define CS0 0
#define CS1 1
#define CS2 2
// Read the AD conversion result
unsigned int read_adc(unsigned char adc_input)
{
ADMUX=adc_input | (ADC_VREF_TYPE & 0xff);
// Delay needed for the stabilization of the ADC input voltage
delay_us(10);
// Start the AD conversion
ADCSRA|=0x40;
// Wait for the AD conversion to complete
while ((ADCSRA & 0x10)==0);
ADCSRA|=0x10;
return ADCW;
}
// global variables
unsigned int adc_code,old_code=1, pwm;
unsigned char string[10];//масив знаків для виводу на дисплей
void main(void)
{
// ADC initialization
// ADC Clock frequency: 250,000 kHz
// ADC Voltage Reference: AVCC pin
ADMUX=ADC_VREF_TYPE & 0xff;
ADCSRA=0x85;

DDRB=0xFF;//Порт Б на виход
//Fast PWM 8-bit
//OC1A встановлюється в 1 при свівпаданні (COM1A1=1, COM1A0=1)
//OC1B  скидається в 0 при свівпаданні (COM1B1=1, COM1B0=0)
TCCR1A=(1<<COM1A1)|(1<<COM1A0)|(1<<COM1B1)|(1<<WGM10);
TCCR1B=(1<<CS0)|(1<<WGM12);
// LCD module initialization
lcd_init(16);// Дисплей на 16 символів
lcd_clear();// Очищаємо дисплей
lcd_gotoxy(0,0);// Ставим курсор на 0 знак та 0 стрічку
lcd_putsf(" LCD PWM Control ");//Вивод повідомлення на нульову стрічку (першу)
lcd_gotoxy(0,1);// Ставим курсор на 0 знак та 0 стрічку
lcd_putsf("PWN 0-100%:    %");//Вивод повідомлення на нульову стрічку (першу)
delay_ms(100);
while (1)
      {
     adc_code=read_adc(0)/4;// Зчитуємо результат АЦП
     if(adc_code!=old_code) //Якщо значення з АЦП змінилось
    { 
     old_code=adc_code;
     pwm=(adc_code*100)/255;//Перетворення коду 0-1023 в 0-100% для виведення на lcd 
     sprintf(string, "%d",pwm);//формуємо стрінг із змінної "pwm" 
                                    //типу int в десятковому вигляді
     //заносим значення в регістри порівняння
       OCR1A=adc_code;   
       OCR1B=adc_code;
       lcd_gotoxy(12,1);               
       lcd_puts(string);// Виводимо значення змінної "pwm"
     }                  
     delay_ms(100);
       };
}







Аналого-цифровий перетворювач, вольтметр на базі мікроконтролера ADC Atmega8 CodeVision AVR





#include <mega8.h>
#include <delay.h>
#include <stdio.h>

#asm
   .equ __lcd_port=0x18 ;PORTB
#endasm
#include <lcd.h>

#define ADC_VREF_TYPE 0x40// Опорна напруга АЦП з ножки AVCC

// Read the AD conversion result
unsigned int read_adc(unsigned char adc_input)
{
ADMUX=adc_input | (ADC_VREF_TYPE & 0xff);
// Delay needed for the stabilization of the ADC input voltage
delay_us(10);
// Start the AD conversion
ADCSRA|=0x40;
// Wait for the AD conversion to complete
while ((ADCSRA & 0x10)==0);
ADCSRA|=0x10;
return ADCW;
}

// global variables
float adc_code, voltage;
unsigned char string[4];//масив знаків для виводу на дисплей
void main(void)
{
// ADC initialization
// ADC Clock frequency: 250,000 kHz
// ADC Voltage Reference: AVCC pin
ADMUX=ADC_VREF_TYPE & 0xff;
ADCSRA=0x85;
// LCD module initialization
lcd_init(16);// Дисплей на 16 символів
lcd_clear();// Очищаємо дисплей
lcd_gotoxy(0,0);// Ставим курсор на 0 знак та 0 стрічку
lcd_putsf(" LCD voltmeter ");//Вивод повідомлення на нульову стрічку (першу)
lcd_gotoxy(0,1);// Ставим курсор на 0 знак та 0 стрічку
lcd_putsf(" Voltage:     V ");//Вивод повідомлення на нульову стрічку (першу)
delay_ms(100);
while (1)
      {
     adc_code=read_adc(0);// Зчитуємо результат АЦП
     voltage=(adc_code*5)/1024;// Перетворення в напругу
     sprintf(string, "%.2f",voltage);//формуємо стрінг із змінної "voltage" 
                                    //типу float з двома знаками після коми
     lcd_gotoxy(9,1);               
     lcd_puts(string);// Виводимо значення напруги                  
     delay_ms(100);
       };
}






четвер, 15 січня 2015 р.

Робота з рідиннокристалічним дисплеєм Atmega8 + LCD + CodeVision








#include <mega8.h>
#include <delay.h>
#include <stdio.h>
// Інціалізація підключення дисплея
#asm
   .equ __lcd_port=0x18 ; PORTB
#endasm
#include <lcd.h>
unsigned int temp = 32000;//змінна яку будемо виводити
unsigned char string[20];//масив знаків для виводу на дисплей
bit p=0;                //Для зміни рядків

void main(void)
{
 lcd_init(16);//Ініціалізуємо дисплей
 lcd_clear();// Очищаємо дисплей
 lcd_gotoxy(0,0);// Ставим курсор на 0 знак та 0 стрічку
 lcd_putsf("LCD test");//Вивод повідомлення на нульову стрічку (першу)
 lcd_gotoxy(0,1);       //Перехід курсора на першу стрічку
 lcd_putsf("CHISLO...");//Вивод інформаційного повідомлення на першу стрічку(другу)
 lcd_gotoxy(10,1);       // Вивід символів...
 lcd_putchar('A');
 lcd_gotoxy(12,1);
 lcd_putchar(0x41);
 delay_ms(3000);        //Висить заставка
 lcd_clear();          // Очищаємо дисплей
      
while (1)
      {  
    sprintf(string, "temp=%5d\n", temp);//формуємо стрінг із змінної
    lcd_gotoxy(0,p++);                  //%5 - 5 знаків d - в десяткове знакове число
                                         //\n - з переходом на наступну стрічку
    lcd_puts(string);                  
    delay_ms(1000);
     temp++;                             //  Нарощуємо змінну для виведення
      }
}



Матрична клавіатура 3х4 на Atmega8 CodeVision AVR





#define COLUMN 3  // Кількість колонок
#define ROW 4     // Кількість рядків
#include <mega8.h>
#include <delay.h>
unsigned char button;//Номер кнопки (позиційний)
//Значення що подаються для сканування рядків
unsigned char key_tab[ROW] ={0b11111110,
                             0b11111101,
               0b11111011,
        0b11110111};
// Функція опитування клавіатури
unsigned char scan_key(void)
{
unsigned char key_value = 0;
unsigned char i;

for(i = 0;i < ROW;i++)//Виводимо почерзі 0 в рядки
  {
  PORTB = key_tab[i]; 
  delay_us(100);

    switch (PINB & 0xF0)// Зчитуємо значення з порта В
    {                   //та обчислюємо номер кнопки
    case 0b11100000:
    key_value = 1 + i * COLUMN;
    return (key_value);
    case 0b11010000:
    key_value = 2 + i * COLUMN;
    return (key_value);
    case 0b10110000:
    key_value = 3 + i * COLUMN;
    return (key_value);
    break;
    }
  }
}
void main(void)
{
// Масив цифр для індикатора
unsigned char num[10] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F};

    DDRB |= (1 << 3)|(1 << 2)|(1 << 1)|(1 << 0); // Порт вихода
    DDRB &= ~(1 << 7)|(1 << 6)|(1 << 5)|(1 << 4); // Порт входа
    PORTB = 0xF0; // Встановлюємо лог. 1 в порт входа
    DDRD = 0xFF; // Виход на індикатор
    PORTD = 0x00;
    delay_us(100);
while(1)
{
for(button=1;button<10;button++)
    {
     if(scan_key()==button) // Виводим значення натисненої кнопки на індикатор
        PORTD = num[button];
    }
if(scan_key()==11)     // 0 на 11 кнопці
PORTD = num[0];
    }
}


середу, 14 січня 2015 р.

Семисегментний дисплей на MAX7219 Atmega8 CodeVision AVR







//****************************************************
#include <mega8.h>
#include <delay.h>
//----------------------------------------------------
// Задамо глобальні контанти та змінні
#define PIN_SCK PORTB5  // Тактовий вихід Master_SPI 
#define PIN_MOSI PORTB3 // Вихід даних Master_SPI 
#define PIN_SS PORTB2   // Вибір кристала (завантаження на вихід)  
#define PIN_SS1 PORTB1  // Вибір кристала для паралельного запису
                        //(можна використовувати для збільшення швидкодії)
#define ON 1            
#define OFF 0

#define cs_lo PORTB |= (1<<PIN_SS) // 1 на SS дані можуть записуватись в регістр зсуву (16-розрядн.)  
#define cs_hi PORTB &= ~(1<<PIN_SS)// 0 на SS активний вибір кристала 
                                //(завантаження записаних даних на порт)

#define cs1_lo PORTB |= (1<<PIN_SS1) //те саме для паралельного запису 
#define cs1_hi PORTB &= ~(1<<PIN_SS1)
//------------------------------------------------------
//Адреси регістрів настройки МАХ7219
//------------------------------------------------------
#define MAX7219_MODE_DECODE 0x09    // Декадний режим
#define MAX7219_MODE_INTENSITY 0x0A // Яскравість 
#define MAX7219_MODE_SCAN_LIMIT 0x0B// Розряднісь дисплея
#define MAX7219_MODE_POWER 0x0C     // Вимкнення живлення
#define MAX7219_MODE_TEST 0x0F      // Тестовий режим
#define MAX7219_MODE_NOOP 0x00      // Пуста операція
       //Адреси регістрів сегментів
const unsigned char MAX7219_DIGIT[8]={0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08};

#define MAX7219_CHAR_BLANK 0xF     // пустота
#define MAX7219_CHAR_NEGATIVE 0xA  // мінус "-" 
#define BYTE_SYMBOL 0x08          //Максимальна кількість символів

unsigned char active_digit = 8;//к-ть активних розрядів дисплея 
unsigned char number_of_displays=2;//к-ть активних дисплеїв 
unsigned char i,j;// лічильники

//-------------------------------------------------------------------------
//Фукнція запису по SPI (апаратний)
//-------------------------------------------------------------------------
void spiSendByte (char databyte)
{
SPDR = databyte; //записуємо переданий байт в регістр SPI
while (!(SPSR & (1 << SPIF)));//Якщо все передали флаг встановлюється і ми виходими з функції
}
//-----------------------------------------------------------------------------------------
// Функція запису в MAX7219 (пакет 16-біт!!)
//-----------------------------------------------------------------------------------------
void MAX7219_writeData(char data_register, char data,unsigned char chip)
{
unsigned short k;

cs_hi; //Slave_SPI активний може приймати дані 
spiSendByte(data_register);//старші біти передаємо першими
spiSendByte(data);// їх потіснять на місця біти байта даних
for(k=0;k<2*chip;k++)// якщо кількість каскадованих чіпів більша 1(лічба з 0) 
{// то потрібно подати пару пустих байтів після посилки 
//вони продавлять дані на наступний чіп 
spiSendByte(MAX7219_MODE_NOOP);  // пустота операція 
}// Нічого не робить але зміщає все на один байт 
//не дивуйтесь, спочатку пишеться адреса а потім дані які є адресою? але це вже не грає ніякої ролі бо операція пуста!!
// головне зміщення на 2 байта а все інше не міняється! 
cs_lo; // Закриваємо Slave_SPI
}

/*Функція очистки дисплея може колись знадобиться...
void clear_display()
{
unsigned char i,j;
for(j=0;j<3;j++)
{
for(i=1;i<=active_digit;i++)
{
MAX7219_writeData(i, MAX7219_CHAR_BLANK,j);
}
}
}
  */   
//------------------------------------------------------------------------------------------
//Функція виводу даних на дисплей через MAX7219
//------------------------------------------------------------------------------------------  
void display(unsigned char data_disp)
{
for(j=0;j<BYTE_SYMBOL;j++){
MAX7219_writeData(MAX7219_DIGIT[j],data_disp,0);
MAX7219_writeData(MAX7219_DIGIT[j],data_disp+1,1);
   }
}
//--------------------------------------------------------------------------------------------
void main(void)
{
unsigned int j;
DDRB |= (1 << PIN_SCK) | (1 << PIN_MOSI) | (1 << PIN_SS)| (1 << PIN_SS1);//виходи для SPI
SPCR |= (1 << SPE) | (1 << MSTR)| (1<<SPR1);// Вмикаємо SPI, Режим мастер, старшим бітом вперед /Швидкість f/64 

for(j=0;j<number_of_displays;j++)// налаштування кожного чіпа МАХ7219
{
    MAX7219_writeData(MAX7219_MODE_DECODE,0xFF,j); // декадний режим включено
    MAX7219_writeData(MAX7219_MODE_SCAN_LIMIT,active_digit-1,j);// Всі сегменти скануються 0-7
    MAX7219_writeData(MAX7219_MODE_INTENSITY, 8,j); // яскравість на половину 17/34 
    MAX7219_writeData(MAX7219_MODE_POWER, ON,j);// вмикаємо всі табла
 }
while(1)
{
    display(i);// Виводимо змінну яку наращуємо кожну секунду
    delay_ms(1000);
    i++;
    if(i==10) i=0;
}
}



вівторок, 13 січня 2015 р.

Годинник на Atmega8 + DS1302


Схема годинника на DS1302 в Proteus




#asm // Асемблерна вставка ініціалізації підключення мікросхеми DS1302 до Atmega8
  .equ __DS1302_port=0x15;//Подключаем DS1302 к PORTС(0x15-адреса регістра порта)
  .equ __DS1302_io=2 ;//Другий пін для io
  .equ __DS1302_sclk=1 ;//Перший пін для sclk
  .equ __DS1302_rst=0 ;//Нульовий пін для rst
  #endasm
#include <bcd.h>
#include <mega8.h>
#include <delay.h>
#include <DS1302.h>

#define DIGITS 6   // Кількість розрядів індикатора
unsigned char chislo[DIGITS]; //масив чисел, що відповідають кожному розряду
unsigned char numder[]= //масив, в якому індексу будут відповідати біти на порту D для кожної цифри
{ 
0b00111111, //цифра 0
0b00000110, //цифра 1
0b01011011, //цифра 2
0b01001111, //цифра 3
0b01100110, //цифра 4
0b01101101, //цифра 5
0b01111101, //цифра 6
0b00000111, //цифра 7
0b01111111, //цифра 8
0b01101111, //цифра 9
0b00000000, //пусто
}; 
unsigned char hour_1, minute_1;//години та хвилини для наведення
unsigned char second, minute, hour, date, month, year;// змінні часу та дати
bit Button_1_state, Button_2_state;
unsigned short i=0; // змінна, що визначає номер розряду який виводиться
unsigned short k, dp; // змінні для крапки
//*****************************************************************************
// преривання по переповненню timer0
interrupt [TIM0_OVF] void timer0_ovf_isr(void)
{
PORTB=0x3f; // погасимо індикатор
    if (i==2||i==4) dp=k;//виводимо крапку на 2 та 4 індикатор
    else  dp=0;
PORTD=((numder[ chislo[i]])|dp); // виведем поточне число    
PORTB=~(1<<i); // вмикаємо відповідний розряд індикатора
if (i++>DIGITS) i=0; // якшо не дійшли до старшого розряду готуєм виведення наступного
  }                    // якшо дійшли до старшого розряду готуєм виведення нульового
//*****************************************************************************
void out_1(unsigned short num)//виведення першої пари знаків (для секунд)
{   chislo[0]=num%10; //одиниці секунд
    chislo[1]=num/10; // десятки
    k=(~k)&0b10000000;// крапка засвічується та гасне кожну секунду
    }
      
void out_2(unsigned short num)//виведення другої пари знаків (для хвилин)
{   chislo[2]=num%10; //одиниці хвилин
    chislo[3]=num/10; // десятки
    }  
void out_3(unsigned short num)//виведення третьої пари знаків (для годин)
{   chislo[4]=num%10; //одиниці годин
    chislo[5]=num/10; // десятки
    }
//****************************************************************************
void main(void)
{
//Порт В
    PORTB=0x00;
    DDRB=0b00111111; //PB0-3 виходи (розряди)
//Порт С
    PORTC=0b00111111;      //включимо підтягуючі резистори на PС0-5
    DDRC=0b00000000; //PС0-3 входи (кнопки + DS1302)
//Порт Д
    PORTD=0b0000000;
    DDRD=0b11111111; //PD0-PD6 виходи (сегменти) 

    TCCR0=0x03; //встановлюємо подільник таймера 64
    rtc_init(0,0,0);//Ініціалізація DS1302 
            // кожне перерив приблизно 2 мс
    rtc_set_date(13,01,15);//встановлюємо дату 13.01.15         

    TIMSK=0x01;// переривання по переповненню таймера 0   
    #asm("sei")// глобальний дозвіл перерывання
//***************************************************************************
while (1) //основний цикл
{ 
rtc_get_time(&hour,&minute,&second);//зчитуємо години, хвилини, секунди
                                    // у відповідні змінні
    out_1(second);    //виводимо на індикатор секунди
    out_2(minute);    //хвилини
    out_3(hour);     // години    
   
 while (PINC.3==0){   //Поки натиснена кнопка збільшення хвилин
      k=0x80;// крапки засвічуються
  minute++;         // збільшуємо хвилини кожні 300мс в діапазоні 0-60
  if(minute==60)minute=0;
  minute_1 = bin2bcd(minute);// перетворюємо двійкове число в двійково-десяткове
  out_2(minute);//виводимо на індикатор хвилини які наводимо
  delay_ms(300);
  Button_1_state = 1;
      
  }
 while (PINC.4==0){ //Поки натиснена кнопка збільшення годин
      k=0x80;// крапки засвічуються
  hour++;      // збільшуємо години кожні 300мс в діапазоні 0-24
  if(hour==24)hour=0;
  hour_1 = bin2bcd(hour);
  out_3(hour);    //виводимо на індикатор
  delay_ms(300);
  Button_2_state = 1;
    }
    delay_ms(500);
    if (Button_1_state && PINC.3){// Якщо кнопка наведення хвилин була натиснена і її тільки що відпустили 
        ds1302_write(0b10000010, minute_1);//Записуємо нове виставлене значення хвилин в 1302
        ds1302_write(0b10000000, 0);//Скидаємо секунди в 0 в 1302
        Button_1_state = 0;// скидаємо фіксатор
        }
    if (Button_2_state && PINC.4){// Якщо кнопка наведення годин була натиснена і її тільки що відпустили 
        ds1302_write(0b10000100, hour_1);//Записуємо нове виставлене значення годин в 1302
        Button_2_state = 0;
        }
    while (PINC.5==0){ // Поки кнопка дати натиснена 
    k=0;// розділові крапки засвічуються
    rtc_get_date(&date, &month, &year); // зчитується дата
    out_1(year);    //виводимо на індикатор рік
    out_2(month);    //місяць
    out_3(date);     //число 
    delay_ms(500);
 }  
 }   
}

Sourse_file (Proteus in folder EXE)








понеділок, 12 січня 2015 р.

Годинник на індикаторі 7 segment AVR

Годинниковий кварцевий резонатор 
   Розглянемо приклад побудови цифрового годинника на семисегментному індикаторі, який буде відображати години : хвилини : секунди та розділові крапки між ними. Для написання програми скористаємось шаблоном який використовували для роботи із семисегментним індикатором (посилання). Для побудови проекту будемо використовувати шестирозрядний індикатор. Головна відмінність в тому, що для точного відрахування секунд не підходить вбудований RC-генератор за рахунок своєї нестабільності, в мікроконтроллері можна для забезпечення необхідної точності використати таймер 2 в асинхронному режимі коли він тактується від окремого кварцевого високостабільного генератора з частотою 32768 Гц. Такий генератор ми можемо отримати, якщо до виводів контролера підєднати резонатор з частотою  32768 Гц. Такі резонатори ще називають годинникові. Як видно 32768 = (2^15) це число можна розписати як 32768 = 256*128 запамятаємо це значення. Нижче наведений текст програми в CodeVisionAVR.  Налаштування таймера 2 в асинхронному режимі здійснюємо наступним чином: на вході ставимо подільник з коефіцієнтом ділення 256 коли вміст лічильника буде 127 (тобто 128 тіків від 0 до 127) мине рівно одна секунда, тоді генеруємо переривання в якому підраховуємо секунди, хвилини та години.  
    
#include <mega8.h>
#include <delay.h>
#define DIGITS 6   // Кількість розрядів індикатора
unsigned char chislo[DIGITS]; //масив чисел, що відповідають кожному розряду
unsigned char numder[]= //масив, в якому індексу будут відповідати біти на порту D для кожної цифри
{ 
0b00111111, //цифра 0
0b00000110, //цифра 1
0b01011011, //цифра 2
0b01001111, //цифра 3
0b01100110, //цифра 4
0b01101101, //цифра 5
0b01111101, //цифра 6
0b00000111, //цифра 7
0b01111111, //цифра 8
0b01101111, //цифра 9
0b00000000, //пусто
}; 
// змінні, години:хвилини:секунди які виводиться на дисплей
unsigned short old_sek=1, sek=0, old_min=1, min=0, old_chas=5, chas=0;
unsigned short i=0; // змінна, що визначає номер розряду який виводиться
unsigned short k, dp; // змінні для крапки

// преривання по переповненню timer0
interrupt [TIM0_OVF] void timer0_ovf_isr(void)
{
PORTB=0x3f; // погасимо індикатор
    if (i==2||i==4) dp=k;//виводимо крапку на 2 та 4 індикатор
    else  dp=0;
PORTD=((numder[ chislo[i]])|dp); // виведем поточне число    
PORTB=~(1<<i); // вмикаємо відповідний розряд індикатора
if (i++>DIGITS) i=0; // якшо не дійшли до старшого розряду готуєм виведення наступного
  }                    // якшо дійшли до старшого розряду готуєм виведення нульового
// Переривання по співпаданню таймера 2 (відбувається кожну секунду) )
interrupt [TIM2_COMP] void timer2_comp_isr(void){
   sek++;// нарощення секунд
   if(sek==60){
     sek=0;
     min++;// нарощення хвилин
   } 
   if(min==60){
     min=0;
     chas++;// нарощення годин
   }
   if(chas==24){
     chas=0;
     }
   }
void out_1(unsigned short num)//виведення першої пари знаків (для секунд)
{   chislo[0]=num%10; //одиниці секунд
    chislo[1]=num/10; // десятки
    k=(~k)&0b10000000;// крапка засвічується та гасне кожну секунду
    }
      
void out_2(unsigned short num)//виведення другої пари знаків (для хвилин)
{   chislo[2]=num%10; //одиниці хвилин
    chislo[3]=num/10; // десятки
    }  
void out_3(unsigned short num)//виведення третьої пари знаків (для годин)
{   chislo[4]=num%10; //одиниці годин
    chislo[5]=num/10; // десятки
    }
void main(void)
{
//Порт В
PORTB=0x00;
DDRB=0b00111111; //PB0-3 виходи (розряди)
//Порт С
PORTC=0x0f;      //включимо підтягуючі резистори на PС0-3
DDRC=0b00000000; //PС0-3 входи (розряди)
//Порт Д
PORTD=0b0000000;
DDRD=0b11111111; //PD0-PD6 виходи (сегменти) 

TCCR0=0x03; //встановлюємо подільник таймера 64 
            // кожне перерив приблизно 2 мс         
//********* Налаштування таймера 2***************************
ASSR=0b00001000;// включили асинхронний режим
TCNT2=0x00;     //занулення регістра лічильника 
OCR2=127;// число з яким порівнюється вміст лічильника             
TCCR2=0b00000110;//подільник частоти з коеф. 256         
delay_ms(100);// затримка для введення асинхронного лічильника
TCCR2 |=(1<<WGM21);//обнулення лічильника при співпаданні
//************************************************************
TIMSK=0x81;//прерывания по переполнению таймера 0 та співпадання таймера 2
#asm("sei")// глобальний дозвіл перерывання
while (1) //основний цикл
{
if(old_sek!=sek) { //Якщо секунди змінились
    out_1(sek);    //виводимо на індикатор
    old_sek=sek;
    }
 if(old_min!=min) { //Якщо хвилини змінились
    out_2(min);    //виводимо на індикатор
    old_min=min;
    } 
 if(old_chas!=chas) {//Якщо години змінились
    out_3(chas);     //виводимо на індикатор
    old_chas=chas;
    }
 if (PINC.0==0){   //Якщо натиснена кнопка збільшення хвилин
    min++;         // збільшуємо хвилини кожні 200мс в діапазоні 0-60
 if(min==60)min=0;
   delay_ms(200);
  }
  if (PINC.1==0){ //Якщо натиснена кнопка збільшення годин
    chas++;      // збільшуємо години кожні 200мс в діапазоні 0-24
 if(chas==24)chas=0; 
   delay_ms(200);
  }
 }
}


Модель годинника в Proteus






четвер, 8 січня 2015 р.

Робота з семисегментним індикатором Частина_1



  Семисегментний індикатори - це світлодіоди, розміщені в певному порядку, так що запалюючи одночасно кілька світлодіодів, можна формувати на індикаторі символи і цифри. Індикатори бувають із загальним катодом (мінусом) і з загальним анодом (плюсом). Це означає, що всі сім світлодіодів з'єднані плюсом чи мінусом. Наприклад, візьмемо індикатор із загальним катодом і спробуємо на нього що-небудь вивести, наприклад цифру 1. Для цього нам потрібно включити світлодіоди B і С, або для того щоб вивести цифру "6" необхідно подати живлення на світлодіоди сегментів А,C,D,E,F та G. Підключити один семисегментний індикатор і керувати ним за допомогою мікроконтролера процедура нескладна. Для цього достатньо сегменти індикатора підключити до порту мікроконтролера через струмообмежувальні резистори по 250 Ом. Загальний вивод підключити до лінії іншого порту мікроконтролера. Залежно від того яку цифру треба вивести, в порт виводимо двійковий код цієї цифри, посилаючись на тип підключеного індикатора (із загальним анодом або катодом) на загальний провід подаємо плюс або мінус. Для зручності можна зробити таблицю кодів для індикатора. Якщо підключення таке: PD7-h, PD6-g, PD5-f, PD4-e, PD3-d, PD2-c, PD1-b, PD0-a, то для відображення цифри 1 в порт D потрібно вивести такий двійковий код: 0b00000110.
Рисунок 1 - Будова семисегментного індикатора
 із спільним катодом
   Для відображення цифрових даних одного семисегментного індикатора зазвичай недостатньо. У таких випадках до мікроконтролера підключають відразу кілька індикаторів, які зєднані в одну збірку паралельно. Однак, через відсутність достатньої кількості виводів у мікроконтролера застосовують спеціальні методи. Один з таких методів це динамічна індикація. Режим динамічної індикації застосовують для побудови багаторозрядних індикаторів. При такому режимі розряди індикатора працюють не одночасно, а по черзі. Перемикання розрядів відбувається з великою швидкістю ( більше 50 Гц), через це людське око не помічає, що індикатори працюють по черзі. Так як у світлодіодів дуже мала інерційність, що змінюються розряди зливаються в одне зображення. У цьому режимі в кожен момент часу працює тільки один розряд, включаються по черзі починаючи з першого закінчуючи останнім, потім все починається спочатку.

Рисунок 2 - Принцип динамічної індикації індикатора
   Підключити семисегментний індикатор до МК можна по схемі, яка приведена на рисунку 3  в нашому випадку використаємо індикатор  із спільним катодом, також слід зазначити, що виводи керування розрядами (1, 2, 3 та 4) бажано вмикати через транзисторні ключі тоді керування від пінів контролера інвертується.

Рисунок 3 - Схема вмикання індикатора та МК

   Нижче наведена таблиця відповідності коду який подається на порт D МК та цифр, що виводяться на семисегментний дисплей в двійковому та шістнадцятковому форматі.

Цифра PD7 (H) PD6 (G) PD5 (F) PD4 (E) PD3 (D) PD2 (C) PD1 (B) PD0 (A) HEX  
0 0 0 1 1 1 1 1 1 0x3F
1 0 0 0 0 0 1 1 0 0x06
2 0 1 0 1 1 0 1 1 0x5B
3 0 1 0 0 1 1 1 1 0x4F
4 0 1 1 0 0 1 1 0 0x66
5 0 1 1 0 1 1 0 1 0x6D
6 0 1 1 1 1 1 0 1 0x7D
7 0 0 0 0 0 1 1 1 0x07
8 0 1 1 1 1 1 1 1 0x7F
9 0 1 1 0 1 1 1 1 0x6F
h 1 0 0 0 0 0 0 0 0x80

   Програма мікроконтролера в IDE CodeVisionAVR приведена нижче, подробиці розкриті в коментарях, слід відзначити, що в програмі застосований таймер 0, для для формування переривань через рівні відрізки часу в кожному перериванні на індикаторі відображається один символ.  Частоту виникнення переривань можна розрахувати якщо відомо частоту тактового генератора fosc (8МГц), коефіцієнт подільника перед лічильником k (64) та розрядність лічильника 0 n (8):
   Отже, виведення символи на індикатор виводяться по одному по черзі через 2 мс, тобто з частотою 500 Гц чого цілком достатньо для динамічного відображення інформації. 


#include <mega8.h>
#include <delay.h>
#define DIGITS 4   // Кількість розрядів індикатора
unsigned short chislo[DIGITS]; //масив чисел, що відповідають кожному розряду
unsigned short numder[]= //масив, в якому індексу будут відповідати біти на порту D для кожної цифри
{ 
0b00111111, //цифра 0
0b00000110, //цифра 1
0b01011011, //цифра 2
0b01001111, //цифра 3
0b01100110, //цифра 4
0b01101101, //цифра 5
0b01111101, //цифра 6
0b00000111, //цифра 7
0b01111111, //цифра 8
0b01101111, //цифра 9
0b00000000, //пусто
};
unsigned char i=0; // змінна, що визначає номер розряду який виводиться
// преривання по переповненню timer0
interrupt [TIM0_OVF] void timer0_ovf_isr(void)
{
PORTB=0x0f; // погасимо індикатор
PORTD=numder[ chislo[i] ]; // виведем поточне число
PORTB=~(1<<i); // вмикаємо відповідний розряд індикатора
if (i++>DIGITS) i=0; // якшо не дійшли до старшого розряду готуєм виведення наступного
}                    // якшо дійшли до старшого розряду готуєм виведення нульового
void out(unsigned short num)
{ 
    chislo[0]=num%10; //одиниці оператор % дає залишок від цілочисельного ділення, наприклад 34%10 будет 4
    chislo[1]=(num%100)/10; // десятки
    chislo[2]=(num%1000)/100; //сотні
    chislo[3]=num/1000; // тисячі
    if (chislo[3]==0) {chislo[3]=10; // виводимо "пусто" якщо старші розряди =0
      if (chislo[2]==0) {chislo[2]=10;
         if (chislo[1]==0) chislo[1]=10;
        }
       }
      }
void main(void)
{
unsigned int z=0; // змінна, яка виводиться на дисплей
PORTB=0x00;
DDRB=0b00001111; //PB0-3 виходи (розряди)

PORTD=0b0000000;
DDRD=0b01111111; //PD0-PD6 виходи (сегменти) 

TCCR0=0x03; //встановлюємо подільник таймера 64 
            //    кожне перерив приблизно 2 мс
TIMSK=0x01; // прерывания по переполнению таймера 0
#asm("sei") // глобальний дозвіл переривання

while (1) 
{
out(z); // виводимо значення змінної z і збільшуємо її на 1 кожні 200 мс
z++;

delay_ms(200);
 }
}


Працездатність програми підтверджує моделювання в Proteus схема на рисунку 4

Рисунок 4 - Схема моделі семисегментного індикатора в Proteus




суботу, 3 січня 2015 р.

Матричний індикатор


Конструкція матричного індикатора
   Матричний індикатор служить для відображення цифр, символів і спеціальних знаків, графічних зображень в різних пристроях. Виготовляються матричні індикатори різних кольорів (червоні, сині, RGB ..), бувають із загальним анодом (катодом), формою корпусу і розмірів. У матричному дисплеї, кілька світлодіодів з'єднані разом в рядках і стовпцях утворюють матрицю (рисунок 1).
Рисунок 1 - Матричний світлодіодний індикатор 8х8
     Це зроблено, щоб звести до мінімуму кількість виводів, необхідних для керування світлодіодами. Наприклад, світлодіодна матриця розмірністю 8×8  світлодіодів містить 64 світлодіоди, для керування якими потрібно 64 порта вводу/виводу, по одному для кожного пікселя індикатора. Якщо  підключити всі аноди разом в рядах (R1-R8), і катоди в стовпчиках (C1-C8), необхідна кількість портів вводу/виводу зменшується до 16. Кожен світлодіод, адресується номером рядка і стовпчика. Це як в грі "морський бій", наприклад, для того щоб засвітився світлодіод обведений на рисунку 1 необхідно подати "+" на рядок R6 та "-" на стовпчик С3.   На малюнку нижче, якщо R4 витягнув високим і С3 на низький рівень, LED в четвертому ряду і третьому стовпці буде включений. Довільне зображення, що формується пікселями індикатора може бути отримане методом швидкого сканування рядків або стовпців. Розглянемо для прикладу формування зображення методом сканування рядків на матриці 5х5. В цьому випадку сканування здійснюється по рядкам R1-R5 (номери стрічок) на які почергово подається логічна одиниця з високою частотою (>100 Гц). Код для кожної стрічки подається на стовпчики C1-C5, зверніть увагу, що код інверсний бо в стовпчики матриці з'єднанні катоди світлодіодів. На рисунку 2 зображено формування зображення цифри "3" на індикаторі, це зображення можна умовно поділити на стрічки в яким відповідає певний код чергування цих стрічок на високій частоті утворює суцільне зображення на індикаторі.
Рисунок 2 - Формування зображення на індикаторі 5х5
   В динаміці зміну станів індикатора наведено на діаграмах на рисунку нижче, як бачимо використовуючи динамічну індикацію на матричний дисплей можна вивести будь-яку конфігурацію пікселів та відображати різноманітні символи.

Рисунок 3 - Формування зображення цифри "3" на світлодіодній матриці 5х5 в динаміці
   Приступимо до написання програми, використаємо для нашої цілі мікроконтролер Atmega8 та компылятор CodeVisionAVR, спочатку як завжди підключимо необхідні бібліотеки та об'явимо змінні.     

#include <mega8.h>
#include <delay.h>

// Declare your global variables here
unsigned int v = 5;          //змінна яка виводиться на дисплей 
unsigned short k[]  = {0b00001,0b00010,0b00100,0b01000,0b10000};// номери стрічок
unsigned short i3[10][5] = {                                    // стрічки
        {0b1110000,0b1110110,0b1110110,0b1110110,0b1110000}, //0
        {0b1111101,0b1111001,0b1110101,0b1111101,0b1111101}, //1
        {0b1110000,0b1111110,0b1110000,0b1110111,0b1110000}, //2
        {0b1110000,0b1111110,0b1111000,0b1111110,0b1110000}, //3
        {0b1110110,0b1110110,0b1110000,0b1111110,0b1111110}, //4
        {0b1110000,0b1110111,0b1110000,0b1111110,0b1110000}  //5
};






void b(void)
{
        int c=0;
        while(c<5)                      
        {       PORTB=0xff;          //погасимо стрічку
                PORTD=k[c];          //вибір стовбця (правильніше номера стрічки)                PORTB=i3[v][c];      // вибір числа з масиву
                c++;                            
                delay_ms(3);          //затримка
        }
}





void main(void)
{     // Ініціалізація
// Port B initialization
DDRB=0x1F;

// Port C initialization
PORTC=0x00;
DDRC=0x00;

// Port D initialization
PORTD=0x00;
DDRD=0x1F;
// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
ACSR=0x80;
SFIOR=0x00;
    // Основна програма
while (1)
      {
       b();
      }
}