Здравствуйте! Собираю для отца прибор для диагностики автомобильных электроцепей. Использую микроконтроллер Atmega168pa. Суть такая - при нажатии на кнопку меняются режимы работы прибора: 1 - вольтметр, 2 - тестер масса/+ питания, 3 - генератор сигналов, 4 - частотомер и т.д. Имеется трёхразрядный семисегментный индикатор, информация выводится на него в динамическом режиме. Программу пишу на Arduino IDE, заливаю в камень скетч через Arduino ISP.
Кнопка подключена к 23-й ноге атмеги (она же А0/14, она же PC0/PCINT8) через резистор 10К, замыкается на землю. Вначале всё шло как по маслу. Для обработки нажатия кнопки использовал библиотеку GyverButton. Режимы переключались без проблем. Флагом переключения режима служило значение переменной byte А, которое менялось от нажатия на единицу. При очередной прошивке МК вылезла какая-то ошибка AVR Dude, точное описание не помню, но смысл был в том, что произошла ошибка записи. Я решил, что ошибка из-за потерянного контакта с МК, после чего пошевелил провода, проверил контакты и без проблем залил скетч. Но тут начались странности: кнопка переключения режима стала отрабатывать некорректно. То сработает нажатие, то нет. То сработает после долгого нажатия, то срабатывает после нескольких нажатий с разной частотой, то не срабатывает совсем. В общем, поведение кнопки зависит от настроения. Что я только не делал. И библиотеку обработки кнопки менял, и способы отсчета времени внутри скетча, всё бесполезно.
Решил записать простейший скетч для проверки самой кнопки (мало ли, может что с ней):
‘’‘void setup() {
pinMode(5, OUTPUT);
pinMode(14, INPUT);
digitalWrite(5, LOW);
}
void loop() {
A = digitalRead(14);
if (A == 1)
{digitalWrite(5, HIGH);}
}
‘’’
Кнопка работает нормально: при нажатии загорается светодиод, при отпускании кнопки - гаснет. Значит беда в программной части. Решил повешать обработку кнопки на внешнее прерывание, чтобы смене режима не могли помешать никакие затыки в программе. С помощью библиотеки PinChangeInterrupt.h установил внешнее прерывание на пин А0/14, на котором висит кнопка. Залил прошивку - и кнопка заработала без проблем! Переключение режимов производилось чётко без задержек при каждом нажатии… буквально секунд 15. После чего МК снова начал обрабатывать нажатие кнопки фиг пойми как (ДАЖЕ ПРИ ВНЕШНЕМ ПРЕРЫВАНИИ!). Я уже сломал всю голову. Помогите советом! Что может быть не так?
Код на данный момент представляет из себя вот что:
‘’’
#include <PinChangeInterrupt.h>
#define Button_Pin 14
#define AOFF PORTC &= ~ (1 << 5) //Управление пинами, управляющими сегментами индикатора
#define AON PORTC |= (1 << 5)
#define BOFF PORTD &= ~ (1 << 1)
#define BON PORTD |= (1 << 1)
#define COFF PORTD &= ~ (1 << 3)
#define CON PORTD |= (1 << 3)
#define DOFF PORTD &= ~ (1 << 4)
#define DON PORTD |= (1 << 4)
#define EOFF PORTB &= ~ (1 << 6)
#define EON PORTB |= (1 << 6)
#define FOFF PORTD &= ~ (1 << 0)
#define FON PORTD |= (1 << 0)
#define GOFF PORTD &= ~ (1 << 2)
#define GON PORTD |= (1 << 2)
#define H1OFF PORTC |= (1 << 4)
#define H1ON PORTC &= ~ (1 << 4)
#define H2OFF PORTC |= (1 << 3)
#define H2ON PORTC &= ~ (1 << 3)
#define H3OFF PORTC |= (1 << 2)
#define H3ON PORTC &= ~ (1 << 2)
#define DPOFF PORTB &= ~ (1 << 7)
#define DPON PORTB |= (1 << 7)
#define GREENLEDON PORTD |= (1 << 5)
#define GREENLEDOFF PORTD &= ~ (1 << 5)
#define REDLEDON PORTD &= ~ (1 << 6)
#define REDLEDOFF PORTD |= (1 << 6)
#define PERIOD 6
#define PERIOD2 250
#define PERIOD3 250
#define PERIOD4 6
#define PERIOD5 250
#define PERIOD6 6
#define PERIOD7 300
#define PERIOD8 1000
int u = 0; // переменная для числа пребразования 0-1023
int u2 = 0;
float u_in = 0.0; // переменная для напряжения 0.0-5.0
float u_inR = 0.0;
float u_in2 = 0.0;
int Ut, Des, Ed, Dol;
int32_t timer1 = 0;
int32_t timer2 = 0;
int32_t timer3 = 0;
int32_t timer4 = 0;
int32_t timer5 = 0;
int32_t timer6 = 0;
int32_t timer7 = 0;
int32_t timer8 = 0;
byte B, C, D, E, F, G, H, I;
volatile int A = 0;
void setup() {
attachPCINT(digitalPinToPCINT(Button_Pin), ModeChange, FALLING);
DDRD = B11111111; //назначаем все порты D (пины с 0 по 7) как выходы
pinMode(15, OUTPUT);
pinMode(16, OUTPUT);
pinMode(17, OUTPUT);
pinMode(18, OUTPUT);
pinMode(19, OUTPUT);
pinMode(20, OUTPUT);
pinMode(21, OUTPUT);
pinMode(A7, INPUT);
H1ON;
H2OFF;
H3OFF;
DPOFF;
REDLEDOFF;
GREENLEDOFF;
A = 0;
B = 0;
C = 0;
D = 0;
E = 0;
F = 0;
G = 0;
H = 0;
Ut = 0;
I = 0;
}
void ModeChange(void)
{
static unsigned long millis_prev;
if (millis() - 300 > millis_prev)
{millis_prev = millis(); A++;}
if (A > 3) {A = 0;}
}
//коды управления сегментами индикатора для формирования цифры___________________
void DIG0()
{
AON; BON; CON; DON; EON; FON; GOFF;
}
void DIG1()
{
AOFF; BON; CON; DOFF; EOFF; FOFF; GOFF;
}
void DIG2()
{
AON; BON; COFF; DON; EON; FOFF; GON;
}
void DIG3()
{
AON; BON; CON; DON; EOFF; FOFF; GON;
}
void DIG4()
{
AOFF; BON; CON; DOFF; EOFF; FON; GON;
}
void DIG5()
{
AON; BOFF; CON; DON; EOFF; FON; GON;
}
void DIG6()
{
AON; BOFF; CON; DON; EON; FON; GON;
}
void DIG7()
{
AON; BON; CON; DOFF; EOFF; FOFF; GOFF;
}
void DIG8()
{
AON; BON; CON; DON; EON; FON; GON;
}
void DIG9()
{
AON; BON; CON; DON; EOFF; FON; GON;
}
void SYMT()
{
AOFF; BOFF; COFF; DON; EON; FON; GON;
}
void SYMS()
{
AON; BOFF; CON; DON; EOFF; FON; GON;
}
void SYME()
{
AON; BOFF; COFF; DON; EON; FON; GON;
}
void SYMn()
{
AOFF; BOFF; CON; DOFF; EON; FOFF; GON;
}
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^Конец передачи кода цифры ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//Функция переключения цифр в динамической индикации_______________
void SwitchDIG()
{
if (B == 0) //определеяем, какая цифра должна гореть на первом индикаторе (десятки вольт)
{H3OFF; H1ON;
switch(Des) {
case 0: DIG0(); break;
case 1: DIG1(); break;
case 2: DIG2(); break;
case 3: DIG3(); break;
case 4: DIG4(); break;
}}
if (B == 1) //определеяем, какая цифра должна гореть на втором индикаторе (единицы вольт)
{if (I == 0) //с точкой
{
H1OFF; H2ON; DPON;
switch(Ed) {
case 0: DIG0(); break;
case 1: DIG1(); break;
case 2: DIG2(); break;
case 3: DIG3(); break;
case 4: DIG4(); break;
case 5: DIG5(); break;
case 6: DIG6(); break;
case 7: DIG7(); break;
case 8: DIG8(); break;
case 9: DIG9(); break;}
}
if (I == 1) //без точки
{
H1OFF; H2ON; DPOFF;
switch(Ed) {
case 0: DIG0(); break;
case 1: DIG1(); break;
case 2: DIG2(); break;
case 3: DIG3(); break;
case 4: DIG4(); break;
case 5: DIG5(); break;
case 6: DIG6(); break;
case 7: DIG7(); break;
case 8: DIG8(); break;
case 9: DIG9(); break;}
}
}
if (B == 2) //определеяем, какая цифра должна гореть на третьем индикаторе (десятые доли вольт)
{H2OFF; DPOFF; H3ON;
switch(Dol) {
case 0: DIG0(); break;
case 1: DIG1(); break;
case 2: DIG2(); break;
case 3: DIG3(); break;
case 4: DIG4(); break;
case 5: DIG5(); break;
case 6: DIG6(); break;
case 7: DIG7(); break;
case 8: DIG8(); break;
case 9: DIG9(); break;
}}
B++;
if (B == 3)
{B = 0;}
}
void PrintTst()
{
if (D == 0) {H3OFF; H1ON; SYMT();}
if (D == 1) {H1OFF; H2ON; SYMS();}
if (D == 2) {H2OFF; H3ON; SYMT();}
D++;
if (D == 3) {D = 0;}
}
void PrintGen()
{
if (E == 0) {H3OFF; H1ON; DIG6();}
if (E == 1) {H1OFF; H2ON; SYME();}
if (E == 2) {H2OFF; H3ON; SYMn();}
E++;
if (E == 3) {E = 0;}
}
void voltmeter()
{
if (C == 0) {GREENLEDOFF; REDLEDOFF; C = 1; I=0;} //флаг нажатия
if (millis() - timer2 >= 250) {
timer2 = millis();
u = analogRead(A7); // -------------------------------------измерение и калибровка
u_in = (u * 5.0) / 1023 * 10; // пересчет измерения в вольты
if (u_in >= 3.5)
{u_inR = u_in * 1.128;}
if (u_in >= 1.0 && u_in < 3.5)
{u_inR = u_in * 1.27;}
if (u_in < 1.0)
{u_inR = 0.0;} //-------------------------------------------конец измерения и калибровки
Ut = u_inR * 10;
Des = Ut / 100 % 10;
Ed = Ut / 10 % 10;
Dol = Ut % 10;
}
if (millis() - timer1 >= 6) { //переключение индикаторов с периодичностью 25 раз в секунду + начало защиты от пропуска шага
timer1 = millis();
SwitchDIG();
}
}
void tester()
{
if (C ==1) {GREENLEDON; DPOFF; C = 2;} //флаг нажатия
if (millis() - timer3 >= PERIOD3) { // измерение напряжения для решения о зажигании красного светодиода и подачи звукового сигнала
timer3 = millis();
u = analogRead(A7); // измерение
u_in = (u * 5.0) / 1023 * 10; // пересчет измерения в вольты
if (u_in > 11.0)
{REDLEDON; tone(15, 1000);}
else {REDLEDOFF; noTone(15);}
} //---------------------------------------------
if (millis() - timer5 >= PERIOD5) { // измерение напряжения для решения о зажигании зеленого светодиода и подачи звукового сигнала
timer5 = millis();
u2 = analogRead(A6); // измерение
u_in2 = (u2 * 5.0) / 1023; // пересчет измерения в вольты
if (u_in2 <=3.9)
{tone(15, 300);}
// else {noTone(15);}
} //-----------------------------------------------
if (millis() - timer4 >= PERIOD4) { //отображение надписи TST на индикаторе (динамическая индикация)
timer4 = millis();
PrintTst();
}
}
void generator()
{
if (C == 2) {GREENLEDOFF; C=3;}
if (millis() - timer6 >= PERIOD6) { //отображение надписи TST на индикаторе (динамическая индикация)
timer6 = millis();
PrintGen();
// tone(5, 1000);
//delay(80);
// noTone(5);
//delay(80);
}
}
void chastotomer()
{
if (C == 3) {REDLEDON; C=0; I=1;}
if (H == 0)
{u = analogRead(A7);
u_in = (u * 5.0) / 1023 * 10; // пересчет измерения в вольты
if (u_in >= 1.6) {G++; H=1;}}
if (H == 1)
{u = analogRead(A7);
u_in = (u * 5.0) / 1023 * 10; // пересчет измерения в вольты
if (u_in < 1.6) {H=0;}}
if (millis() - timer8 >= PERIOD8) { // измерение напряжения для решения о засчёте очередного герца
timer8 = millis();
Des = G / 100 % 10;
Ed = G / 10 % 10;
Dol = G % 10;
G = 0;}
if (millis() - timer1 >= 6) { //переключение индикаторов с периодичностью 25 раз в секунду + начало защиты от пропуска шага
timer1 = millis();
SwitchDIG();
}
}
//void yield()
//{
//if (millis() - timer6 >= PERIOD6) { //отображение надписи TST на индикаторе (динамическая индикация)
// timer6 = millis();
// PrintGen();
//}
//}
void loop() {
switch(A) {
case 0: voltmeter(); break; //----------------------------------режим по умолчанию-ВОЛЬТМЕТР------------
case 1: tester(); break; //-----------------------------------1-е нажатие кнопки-ТЕСТЕР--------------
case 2: generator(); break; //----------------------------------2-е нажатие кнопки-ГЕНЕРАТОР-------------
case 3: chastotomer(); break; //---------------------------------3-e нажатие кнопки-ЧАСТОТОМЕР-----------
}
} //--------------------------END-----------------------------
‘’’
Кстати, пытался написать скетч с использованием чисто библиотеки обработки кнопки, где весь смысл программы - поочередное включение и выключение светодиода по нажатию кнопки - МК упорно отказывается это делать после того проглюка во время загрузки скетча. Пытался использовать библиотеки Button.h, GyverButton.h, TroykaButton.h - бесполезно. Управление светодиодом напрямую при считывании сигнала с кнопки работает.