Міністерство освіти та науки України
Національний університет “Львівська політехніка”
Звіт
про виконання лабораторної роботи №5
з курсу “Мікропроцесорні пристрої ”
Виконав:
ст. гр. ІБ – 42
Львів – 2009
// Блок управління системи контролю доступу на МК АТ90S2313 з iButton DS1990, I2C-RTC DS1307 та
// SPI-EEPROM 25LC256 (32Kx8)
// Тактова частота МК 7.3728 МГц
////////////////////////////////////////////////////////////////////////////
// Порт для підключення iButton DS1990
#asm
.equ __w1_port = 0x18
.equ __w1_bit = 5
#endasm
////////////////////////////////////////////////////////////////////////////
// Порт для підключення I2C-RTC DS1307
#asm
.equ __i2c_port = 0x18
.equ __scl_bit = 2
.equ __sda_bit = 1
#endasm
////////////////////////////////////////////////////////////////////////////
#include <90S2313.h>
#include <1wire.h>
#include <i2c.h>
#include <delay.h>
////////////////////////////////////////////////////////////////////////////
// Оголошення типу даних - байт
typedef unsigned char byte;
////////////////////////////////////////////////////////////////////////////
// Структура дати і часу
typedef struct
{
byte Second;
byte Minute;
byte Hour;
byte Day;
byte Date;
byte Month;
byte Year;
} DS1307_Data;
/////////////////////////////////////////////////////////////////////////////
// Глобальні змінні
// Оголошення змінної для збереження поточного часу і дати
DS1307_Data DS1307_1;
// Оголошення змінної для збереження ідентифікаційного номера iButton DS1990A
byte ROM_Code[9];
////////////////////////////////////////////////////////////////////////////
//******************************************************************//
// Виводи SPI-EEPROM 25LC256
#define PORT_EEPROM PORTD
#define DDR_EEPROM DDRD
#define PIN_EEPROM PIND
#define CS 3
#define SCK 4
#define SI 5
#define SO 6
//******************************************************************//
// Виводи I2C-RTC DS1307
#define PORT_RTC PORTB
#define DDR_RTC DDRB
#define PIN_RTC PINB
#define SDA 1
#define SCL 2
//******************************************************************//
// Виводи iButton DS1990A
#define PORT_iButton PORTB
#define DDR_iButton DDRB
#define PIN_iButton PINB
#define Data_iButton 5
//******************************************************************//
// Виводи електронного замка
#define PORT_Switch PORTB
#define DDR_Switch DDRB
#define PIN_Switch PINB
#define Switch 7
//******************************************************************//
// Виводи світлодіоду
#define PORT_Led PORTB
#define DDR_Led DDRB
#define PIN_Led PINB
#define Led 6
//******************************************************************//
// Виводи кнопки виходу
#define PORT_Out_Key PORTD
#define DDR_Out_Key DDRD
#define PIN_Out_Key PIND
#define Out_Key 2
//******************************************************************//
// Коди команд EEPROM 25LC256
#define EEPROM_READ 0x03
#define EEPROM_WRITE 0x02
#define EEPROM_WRDI 0x04
#define EEPROM_WREN 0x06
#define EEPROM_RDSR 0x05
#define EEPROM_WRSR 0x01
//******************************************************************//
// Коди команд iButton DS1990
#define SEARCH_ROM 0xF0
//******************************************************************//
// Адреса сімейства iButton
#define DS1990_FAMILY_CODE 0x01
//******************************************************************//
// Кількість подій в системі
#define N_Record 100
//******************************************************************//
// Розмір одного запису в байтах
#define Record_Size 16
//******************************************************************//
// Адреса початку записів в EEPROM 25LC256
#define Record_Addr 0x40
//******************************************************************//
// Адреса лічильника записів в EEPROM
#define Record_Count_Addr 0x30
//******************************************************************//
// Час відкривання дверей в мілісекундах
#define T_msec 7000
//******************************************************************//
// Кількість зареєстрованих ключів
#define N_Key 2
//******************************************************************//
// Тип запису
#define Input_Record 0
#define Output_Record 1
//******************************************************************//
// E8C52A
// 3D2C90
/*----------------------------------------------------------------*/
// Підпрограма обслуговування зовнішнього переривання по виводу INT0
interrupt [2] void ext_interrupt0(void);
/*----------------------------------------------------------------*/
void Pin_Init(void);
// Функції нижнього рівня роботи з ГРЧ
void DS1307_Init(void);
void DS1307_Read(DS1307_Data* arg1);
void DS1307_Write(DS1307_Data* arg1);
byte SPI_Send_Byte(byte data);
void SPI_Send_CMD1(byte cmd);
byte SPI_Read_SR(void);
byte SPI_Read_Data(int address, byte* buffer, byte n);
byte SPI_Write_Data(int address, byte* buffer, byte n);
void Save_Record( byte* code,DS1307_Data* arg1, byte type);
///////////////////////////////////////////////////////////////////////////
void main(void)
{
byte temp, ii, jj, temp1;
byte iButton_Arrary[N_Key][8];
Pin_Init();
DS1307_1.Second = 0x21; // Початкова дата: 10-07-08 23:55:55
DS1307_1.Minute = 0x56;
DS1307_1.Hour = 0x15;
DS1307_1.Day = 0x02;
DS1307_1.Date = 0x07;
DS1307_1.Month = 0x07;
DS1307_1.Year = 0x09;
DS1307_Init();
DS1307_Write(&DS1307_1);
// Прочитати зареєстровані ключі з EEPROM-пам'ті
for(ii = 0; ii < N_Key; ii++)
temp = SPI_Read_Data(0x0000 + ii * 8, &iButton_Arrary[ii][0], 8);
// Налаштувати зовнішнє переривання INT0 від кнопки виходу
GIMSK = GIMSK | 0x40;
MCUCR = MCUCR | 0x02;
while(1)
{
#asm("cli")
temp = w1_search(SEARCH_ROM, &ROM_Code[0]);
if (temp == 0)
{
#asm("sei")
}
else
{
// Пошук
for(ii = 0; ii < N_Key; ++ii)
{
temp1 = 0;
for(jj = 0; jj < 8; ++jj)
{
if(ROM_Code[jj] != iButton_Arrary[ii][jj])
{
temp1 = 1;
break;
}
}
if(temp1 == 0)
break;
}
if(temp1 == 0)
{
DS1307_Read(&DS1307_1);
Save_Record( &ROM_Code[0],&DS1307_1, Input_Record);
PORT_Led.Led = 0;
PORT_Switch.Switch = 1;
delay_ms(T_msec);
PORT_Led.Led = 1;
PORT_Switch.Switch = 0;
}
else
#asm("sei");
}
delay_ms(1);
};
};
//////////////////////////////////////////////////////////////////////////
void DS1307_Init(void)
{
// Ініціалізація І2С шини та режиму роботи годинника реального часу (RTC)
byte temp;
temp = i2c_start(); // Сформувати умову START
temp = i2c_write(0xd0); // Задати адресу пристрою та операцію запису (R/W=0)
temp = i2c_write(0x07); // Задати адресу регістра управління 0х07
temp = i2c_write(0b00000000); // Включити RTC
i2c_stop(); // Сформувати умову STOP
};
///////////////////////////////////////////////////////////////////////////
void DS1307_Read(DS1307_Data* arg1)
{
// Зчитування часу та дати з RTC
byte temp2;
temp2 = i2c_start(); // Сформувати умову START
temp2 = i2c_write(0xd0); // Задати адресу пристрою та операцію запису (R/W=0)
temp2 = i2c_write(0x00); // Задати початкову адресу регістра 0х00
i2c_stop(); // Сформувати умову STOP
temp2 = i2c_start(); // Сформувати умову START
temp2 = i2c_write(0xd1); // Задати адресу пристрою та операцію читання (R/W=1)
arg1->Second = i2c_read(1) & 0x7F; // Прочитати регістр секунд
arg1->Minute = i2c_read(1) & 0x7F; // Прочитати регістр хвилин
arg1->Hour = i2c_read(1) & 0x3F; // Прочитати регістр годин
arg1->Day = i2c_read(1) & 0x07; // Прочитати регістр дня
arg1->Date = i2c_read(1) & 0x3F; // Прочитати регістр числа
arg1->Month = i2c_read(1) & 0x1F; // Прочитати регістр місяця
arg1->Year = i2c_read(0); // Прочитати регістр року та сформувати біт NACK
i2c_stop(); // Сформувати умову STOP
};
//////////////////////////////////////////////////////////////////////////
void DS1307_Write(DS1307_Data* arg1)
{
// Запис часу та дати в RTC
byte temp2;
temp2 = i2c_start(); // Сформувати умову START
temp2 = i2c_write(0xd0); // Задати адресу пристрою та операцію запису (R/W=0)
temp2 = i2c_write(0x00); // Задати початкову адресу регістра 0х00
temp2 = i2c_write(arg1->Second); // Записати значення в регістр секунд
temp2 = i2c_write(arg1->Minute); // Записати значення в регістр хвилин
temp2 = i2c_write(arg1->Hour | 0b00000000); // Записати значення в регістр годин
temp2 = i2c_write(arg1->Day); // Записати значення в регістр днів
temp2 = i2c_write(arg1->Date); // Записати значення в регістр дати
temp2 = i2c_write(arg1->Month); // Записати значення в регістр місяця
temp2 = i2c_write(arg1->Year); // Записати значення в регістр року
i2c_stop(); // Сформувати умову STOP
};
//////////////////////////////////////////////////////////////////////////
byte SPI_Send_Byte(byte data)
{
byte ii, temp, result = 0, temp1 = 0x80;
for(ii = 0; ii < 8; ++ii)
{
temp = PIN_EEPROM;
temp = temp & (1<<SO);
if(temp != 0)
result = result | temp1;
PORT_EEPROM.SI = (data & temp1) && 1;
#asm("nop");
PORT_EEPROM.SCK = 1;
temp1 = temp1 >> 1;
PORT_EEPROM.SCK = 0;
}
return result;
};
//////////////////////////////////////////////////////////////////////////
void Pin_Init(void)
{
PORT_EEPROM.CS = 1;
PORT_EEPROM.SCK = 0;
PORT_EEPROM.SI = 0;
PORT_EEPROM.SO = 0;
DDR_EEPROM.CS = 1;
DDR_EEPROM.SCK = 1;
DDR_EEPROM.SI = 1;
DDR_EEPROM.SO = 0;
PORT_Switch.Switch = 0;
DDR_Switch.Switch = 1;
PORT_Led.Led = 1;
DDR_Led.Led = 1;
PORT_Out_Key.Out_Key = 0;
DDR_Out_Key.Out_Key = 0;
};
//////////////////////////////////////////////////////////////////////////
byte SPI_Write_Data(int address, byte* buffer, byte n)
{
byte ii, result;
PORT_EEPROM.CS = 0;
SPI_Send_Byte(EEPROM_WRITE);
SPI_Send_Byte(address>>8);
SPI_Send_Byte(address);
for(ii = 0; ii < n; ++ii)
SPI_Send_Byte(buffer[ii]);
PORT_EEPROM.SI = 0;
PORT_EEPROM.CS = 1;
return result;
};
//////////////////////////////////////////////////////////////////////////
byte SPI_Read_Data(int address, byte* buffer, byte n)
{
byte ii, result;
PORT_EEPROM.CS = 0;
SPI_Send_Byte(EEPROM_READ);
SPI_Send_Byte(address>>8);
SPI_Send_Byte(address);
for(ii = 0; ii < n; ii++)
buffer[ii] = SPI_Send_Byte(0);
PORT_EEPROM.SI = 0;
PORT_EEPROM.CS = 1;
return result;
};
/////////////////////////////////////////////////////////////////////////
void SPI_Send_CMD1(byte cmd)
{
PORT_EEPROM.CS = 0;
SPI_Send_Byte(cmd);
PORT_EEPROM.SI = 0;
PORT_EEPROM.CS = 1;
};
//////////////////////////////////////////////////////////////////////////
byte SPI_Read_SR(void)
{
byte temp;
PORT_EEPROM.CS = 0;
temp = SPI_Send_Byte(EEPROM_RDSR);
temp = SPI_Send_Byte(0);
PORT_EEPROM.SI = 0;
PORT_EEPROM.CS = 1;
return temp;
};
//////////////////////////////////////////////////////////////////////////
interrupt [2] void ext_interrupt0(void)
{
byte ii;
delay_ms(10); // Затримка 10 мс для усунення явища тремтіння контактів
if(PIN_Out_Key.Out_Key == 0) // Якщо натиснуто кнопку відкривання дверей
{
DS1307_Read(&DS1307_1); // Прочитати часі і дату з RTC
for(ii = 0; ii < 8; ++ii)
ROM_Code[ii] = 0; // В поле коду iButton занести 0
Save_Record( &ROM_Code[0], &DS1307_1, Output_Record); // Зробити запис
PORT_Led.Led = 0; // Включити світлодіод
PORT_Switch.Switch = 1; // Включити електронний замок
delay_ms(T_msec); // Затримка
PORT_Led.Led = 1; // Виключити світлодіод
PORT_Switch.Switch = 0; // Виключити електронний замок
}
};
///////////////////////////////////////////////////////////////////////////
void Save_Record( byte* code, DS1307_Data* arg1, byte type)
{
byte temp, temp1;
int start;
while((temp = SPI_Read_SR()) & 0x01) // Очікувати готовності EEPROM
;
// Прочитати значення лічильника подій в змінну temp1
temp = SPI_Read_Data(Record_Count_Addr, &temp1, 1);
if(temp1 == N_Record) // Якщо досягнуто максимального значення
{
temp1 = 1; // Почати заново
start = Record_Addr;
}
else // Інакше обчислити адресу наступного запису
{
start = Record_Addr + Record_Size * (int)temp1;
temp1++; // Збільшити кількість записів на 1
}
SPI_Send_CMD1(EEPROM_WREN); // Дозволити запис в EEPROM
while((temp = SPI_Read_SR()) & 0x01) // Очікувати готовності EEPROM
;
// Записати 8 байт ідентифікаційного номеру DS1990A
temp = SPI_Write_Data(start, code, 8);
while((temp = SPI_Read_SR()) & 0x01) // Очікувати готовності EEPROM
;
SPI_Send_CMD1(EEPROM_WREN); // Дозволити запис в EEPROM
while((temp = SPI_Read_SR()) & 0x01) // Очікувати готовності EEPROM
;
temp = SPI_Write_Data(start + 8, arg1, 6); // Записати час і дату події
while((temp = SPI_Read_SR()) & 0x01) // Очікувати готовності EEPROM
;
SPI_Send_CMD1(EEPROM_WREN); // Дозволити запис в EEPROM
while((temp = SPI_Read_SR()) & 0x01) // Очікувати готовності EEPROM
;
temp = SPI_Write_Data(start + 15, &type, 1); // Записати тип події: вхід чи вихід
while((temp = SPI_Read_SR()) & 0x01) // Очікувати готовності EEPROM
;
SPI_Send_CMD1(EEPROM_WREN); // Дозволити запис в EEPROM
while((temp = SPI_Read_SR()) & 0x01) // Очікувати готовності EEPROM
;
temp = SPI_Write_Data(Record_Count_Addr, &temp1, 1); // Записати нове значення лічильника
};
Висновок: на лабораторній роботі я ознайомився з принципами побудови модулів управління систем контролю доступу. Вивчив правила обміну інформацією через інтерфейси та ознайомився з їх програмною реалізацією для AVR-мікроконтроллерів на мові C в середовищі CodeVisionAVR.