Третий свой урок я начинал с вопроса "А какую конструкцию на основе МК хотите создать ВЫ?"
Ответов было не много...
Поэтому был создан кодовый замок с возможностью смены кода с клавиатуры.
Код хранится в энергонезависимой памяти микроконтроллера.
А в режим его изменения входим продолжительным нажатием на кнопку "*".
Список всех частей:Грызём микроконтроллеры. Урок 1. Моргаем 8-ю светодиодами. CodeVision, Proteus, ISISГрызём микроконтроллеры. Урок 2. CodeVision и СГрызём микроконтроллеры. Урок 3. Циклы, прерывания и массивыГрызём микроконтроллеры. Урок 4. Мерим температуру или напряжениеГрызём микроконтроллеры. Урок 5. Кодовый замокГрызём микроконтроллеры. Урок 6. Прошиваем МКГрызём микроконтроллеры. Урок 7. Подключение к МК кнопок, клавиатуры, энкодераГрызём микроконтроллеры. Урок 8. Программирование кнопок, клавиатуры, энкодераГрызём микроконтроллеры. Урок 9. Клавиатура вглубину
Начнем этот урок с разбора оператора, который я забыл упомянуть в предыдущих статьях.
Оператор SWITCH.Синтаксис:
switch (
ключ)
{
case значение_1:
// операции, выполняемые если
ключ равен
значению_1 break; // выход из оператора
swith, чтобы не выполнять последующие операции
case значение_2:
// операции, выполняемые если
ключ равен
значению_2 break; // выход из оператора
swith, чтобы не выполнять последующие операции
case default: // не обязательный параметр, выполняется если ни одно
значение не совпало с
ключем // операции, выполняемые если
ключ не равен ни одному из
значений break; // выход из оператора
swith, чтобы не выполнять последующие операции
}
Как видно, этот оператор стравнивает
ключ со
значениями, перечисленными после ключевых слов
case и выполняет тот код, который нам требуется.
Так же, стоит усвоить новый тип переменной.
eeprom определение_типа имя_перемнной=
начальное_значение;
Этой командой мы создаем переменную в энергонезависимой памяти
EEPROM микроконтроллера. Начальное значение записывается в ячейки EEPROM только при программировании МК. А обращаться к этой переменной можно из любого места программы.
Но! Не советую часто записывать в
EEPROM новые значения, т.к. эта память выдерживает около
100 000 циклов записи, после чего ее работоспособность производителем не гарантируется.
Поверьте, это не такая уж и большая цифра, если записывать новое значение, допустим, каждый раз при нажатии на какую-либо кнопку. В таком режиме наша конструкция проработает 2-3 года, после чего, даже при полностью исправном МК, потеряет возможность хранить данные без наличия питания...
Если нужно организовать хранение часто меняющейся информации, например, настроек регулятора громкости и тембра, то их лучше хранить в оперативной памяти, а в EEPROM записывать, к примеру, при выключении усилителя
Развернутого описания для самой программы не дам... Лентяй я
Тем долее, всё должно быть понятно по комментариям
Вот, собственно, ее текст:
/*****************************************************
This program was produced by the
CodeWizardAVR V1.25.9 Standard
Automatic Program Generator
© Copyright 1998-2008 Pavel Haiduc, HP InfoTech s.r.l.
http://www.hpinfotech.com
Project :
Version :
Date : 26.12.2008
Author : F4CG
Company : F4CG
Comments:
Chip type : ATtiny2313
Clock frequency : 1,000000 MHz
Memory model : Tiny
External SRAM size : 0
Data Stack size : 32
*****************************************************/
#include <tiny2313.h>
#include <delay.h>
// Alphanumeric LCD Module functions
#asm
.equ __lcd_port=0x18;PORTB
#endasm
#include <lcd.h>
#define ZAMOK PORTB.3
// инициализация энергонезависимой памяти для хранения кода и установка его "0000"
eeprom unsigned char eeprom_kod[4]={'0','0','0','0'};
// массив символов, соответствующих кнопкам на клавиатуре
flash unsigned char buttons[3][4]={{'1','4','7','*'},{'2','5','8','0'},{'3','6','9','#'}};
// переменные для хранения промежуточных данных
unsigned char kod[4], nkod[4], nkod2[4];
unsigned char i,j,k,n,state;
char key_press() // функция опроса клавиатуры
{
while(1)
{
while((~PIND & 0b01111000)!=0); // ничего не делать, пока не отпустят кнопку
for(i=0;i<3;i++) // перебор столбцов
{
PORTD=0xff & ~(0x01 << i);
for(j=3;j<7;j++) // пребор строк
{
if((PIND & (0x01 << j))==0) // проверка нажатия на кнопу
{
delay_ms(100); // пауза для исключения дребезга контактов
if(buttons[i][j-3]=='*') // дополнительная проверка для кнопки смены пароля
{
for(k=0;((PIND & (0x01 << j))==0 && k<100);k++)
{
delay_ms(50);
}
if((PIND & (0x01 << j))==0) // если удерживаем кнопку смены пароля больше 5 сек
{
state=1; // то устанавливаем состояние 1 (смена пароля)
return 0; // и выходим из функции, возвращая вместо символа ноль
}
else return buttons[i][j-3]; // если меньше 5 сек, то возвращаем символ
}
if((PIND & (0x01 << j))==0) return buttons[i][j-3]; // возвращаем символ
}
}
}
}
}
void main(void)
{
// Declare your local variables here
// Crystal Oscillator division factor: 1
#pragma optsize-
CLKPR=0x80;
CLKPR=0x00;
#ifdef _OPTIMIZE_SIZE_
#pragma optsize+
#endif
// Input/Output Ports initialization
// Port A initialization
// Func2=In Func1=In Func0=In
// State2=T State1=T State0=T
PORTA=0x00;
DDRA=0x00;
// Port B initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=Out Func1=Out Func0=Out
// State7=T State6=T State5=T State4=T State3=T State2=0 State1=0 State0=0
PORTB=0x00;
DDRB=0xff;
// Port D initialization
// Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTD=0x78;
DDRD=0x07;
// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: Timer 0 Stopped
// Mode: Normal top=FFh
// OC0A output: Disconnected
// OC0B output: Disconnected
TCCR0A=0x00;
TCCR0B=0x00;
TCNT0=0x00;
OCR0A=0x00;
OCR0B=0x00;
// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: Timer 1 Stopped
// Mode: Normal top=FFFFh
// OC1A output: Discon.
// OC1B output: Discon.
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer 1 Overflow Interrupt: Off
// Input Capture Interrupt: Off
// Compare A Match Interrupt: Off
// Compare B Match Interrupt: Off
TCCR1A=0x00;
TCCR1B=0x00;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;
// External Interrupt(s) initialization
// INT0: Off
// INT1: Off
// Interrupt on any change on pins PCINT0-7: Off
GIMSK=0x00;
MCUCR=0x00;
// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x80;
// Universal Serial Interface initialization
// Mode: Disabled
// Clock source: Register & Counter=no clk.
// USI Counter Overflow Interrupt: Off
USICR=0x00;
// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
ACSR=0x80;
// LCD module initialization
lcd_init(16);
// считываем текущий код из энергонезависимой памяти
kod[0]=eeprom_kod[0];
kod[1]=eeprom_kod[1];
kod[2]=eeprom_kod[2];
kod[3]=eeprom_kod[3];
lcd_clear(); // очищаем дисплей
// выводим приветствие
lcd_putsf("Kodoviy zamok");
lcd_gotoxy(5,1);
lcd_putsf(" v0.1");
// пауза, чтобы можно было прочитать приветствие
delay_ms(8000);
lcd_clear(); // очищаем дисплей
state=0; // устанавливаем состояние 0 (ввод кода)
while (1)
{
switch (state)
{
case 0: // режим "0" - ввод кода
lcd_clear();
lcd_putsf("Vvedite kod:"); // просим ввести код
lcd_gotoxy(0,1); // переводим курсор на начало второй строки
for(n=0;n<4;n++) // опрашиваем клавиатуру и записывам символы
{ // в массив nkode[]
k=key_press();
if(k==0)break; // если ф-я опроса клавиатуры вернула "0", то прекращаем ввод кода
nkod[n]=k; // иначе, сохраняем полученый символ в nkode[]
lcd_putchar('*'); // и выводим на дисплей "*"
}
if(k==0)break; // если ф-я опроса вернула "0", то прервыаем выполнение оператора switch
// в результате он будет выполнен сначала и мы перейдем к смене кода
while(key_press()!='#'); // дожидаемся нажатия "#" в подтверждение ввода кода
// проверяем, совпадает ли ввеленный код с сохраненным в памяти
// если да - открываем замок, а если нет, то ничего не делаем
// потом, в любом случае, переходим к вводу пароля
if(nkod[0]==kod[0] && nkod[1]==kod[1] && nkod[2]==kod[2] && nkod[3]==kod[3])
{
lcd_clear();
lcd_putsf("Kod verniy");
ZAMOK=1;
delay_ms(2500);
ZAMOK=0;
delay_ms(2500);
}
else
{
lcd_clear();
lcd_putsf("Kod ne verniy!");
delay_ms(5000);
}
break; // выходим из оператора switch
case 1: // режим "1" - изменение пароля
lcd_clear();
lcd_putsf("Izmenenie koda");
lcd_gotoxy(0,1);
delay_ms(5000);
lcd_clear();
lcd_putsf("Stariy kod:");
lcd_gotoxy(0,1);
delay_ms(5000);
for(n=0;n<4;n++)
{
k=key_press();
if(k==0)break;
nkod[n]=k;
lcd_putchar('*');
}
if(k==0)break;
while(key_press()!='#');
if(nkod[0]!=kod[0] || nkod[1]!=kod[1] || nkod[2]!=kod[2] || nkod[3]!=kod[3])
{
lcd_clear();
lcd_putsf("Kod ne verniy!");
delay_ms(5000);
state=0;
break;
}
lcd_clear();
lcd_putsf("Noviy kod:");
lcd_gotoxy(0,1);
for(n=0;n<4;n++)
{
k=key_press();
if(k==0)break;
nkod[n]=k;
lcd_putchar('*');
}
if(k==0)break;
while(key_press()!='#');
lcd_clear();
lcd_putsf("Povtorite kod:");
lcd_gotoxy(0,1);
for(n=0;n<4;n++)
{
k=key_press();
if(k==0)break;
nkod2[n]=k;
lcd_putchar('*');
}
if(k==0)break;
while(key_press()!='#');
if(nkod[0]!=nkod2[0] || nkod[1]!=nkod2[1] || nkod[2]!=nkod2[2] || nkod[3]!=nkod2[3])
{
lcd_clear();
lcd_putsf("Ne sovpadayut!");
delay_ms(5000);
state=0;
break; // выходим из оператора switch
}
else
{
// сохраняем новый код в энергонезависимую память
eeprom_kod[0]=nkod2[0];
eeprom_kod[1]=nkod2[1];
eeprom_kod[2]=nkod2[2];
eeprom_kod[3]=nkod2[3];
// обновляем текущий код в оперативной памяти
kod[0]=nkod2[0];
kod[1]=nkod2[1];
kod[2]=nkod2[2];
kod[3]=nkod2[3];
lcd_clear();
lcd_putsf("Kod izmenen!");
delay_ms(5000);
state=0;
}
break; // выходим из оператора switch
}
};
}
Разумеется, тут многое можно доработать. Увеличить длинну кода и возможность вводить код произвольной длинны. Кое-где можно оптимизировать или вообще переделать.
Но эта программа - ничто иное, как пример
простейшей реализации, чтобы помочь освоить МК новичкам!
А вот вопросы задаем на нашем любимом
форуме
Камрад, рассмотри датагорские рекомендации
🌼 Полезные и проверенные железяки, можно брать
Опробовано в лаборатории редакции или читателями.