НАСТРАИВАЕМЫЙ ТАЙМЕР ДЛЯ УПАКОВОЧНОГО СТАНКА

timer3

Пришел на новую работу инженером-схематехником по электрике на тему упаковочных станков. Поначалу изучал как читать электрические схемы и т. д., потом начальство узнало что я немного программист... Слово за слово, как-то так получилось что я в итоге занялся разработкой таймера, который управляет нагревом лезвия, которое, в свою очередь, отрезает и запаивает пленку, типа стреча. Здесь речь пойдет о первом испытательном образце, open-source прототипе, в котором скрывать практически нечего, но следующая, гораздо более продуманная версия, если ей вообще суждено сбыться, будет уже по-настоящему запатентована и ее принципиальное устройство будет засекречено в интересах компании ARD Systems.

timer2 timer4

 

Суть задумки, в кратце, такая: таймер по умолчанию должен быть настроен на то, чтобы быть готовым подать определенную продолжительность высокого уровня сигнала на реле X2, подключенного к ноге микроконтроллера PD5, скажем в 800 миллисекунд. Кнопка SW2 включает режим настройки, в котором можно будет задать некоторые параметры, такие как время этой самой задержки, выбор алгоритма компенсации общего нагрева по данным с датчиков и количеству отработанных итераций, и т. д. Кнопки SW3 и SW4 это + и - соответственно. Далее,  когда замыкается концевик SW1, подключенный к ноге PB3, микроконтроллер как раз и подает этот высокий уровень. Время соответствующей задержки в миллисекундах должно выводиться на четырехразрядный семисегментный индикатор LED1. Все это дело тактируется внешним кварцем 16 MHz.

Первые наброски принципиальной схемы сначала делались в Proteus 8, там же и тестировались, а вот трассировку и гербер-файлы я предпочитаю делать в EasyEDA.

madTimer

Намучился я с ним, пришлось даже съездить в Москву на консультацию к другу-инженеру, но это, правда, мало чего дало. Сейчас уже выпущена новая версия платы и ПО, которе допилено практически до идеала. Там вроде бы немного кода, но он так отполирован, что круче уже не может быть. Пока занимался разработкой, выучил все сука подводные камни с самим микроконтроллером, все эти ебаные прерывания, все сука таймеры и т. д., научился более-менее адекватной разводке PCB (хотя тут всегда будет куда расти)... Фирма оборудование закупила... Тут мне и осцилографы, и всякие мультиметры, и паяльная станция и даже есть станция с нижним подогревом для пайки BGA! Скоро будут заказывать наверно трафареты под ребол. Микроскоп с дома припру, приспособлю под пайку мелких SMD-компонентов... Опыт просто огромный с этим проектом! Просто коллосальный! И теперь думаю нахер больше не хочу связавыться с Атмегами. Впрочем, наверно, не стоит так категорично, потому что для чего-то простенького они норм, и к тому же все уже знакомо...

А на этот конкретный случай таймера сохранился старый рабочий вариант программы:

/*
 * main.c
 * Time-pulse Controller
 * Created: 5/2/2023 2:22:29 PM
 * Author: madmentat
 * ARD Systems, 2023
 */
#include <avr/io.h>
#define F_CPU 16000000UL
#include <avr/interrupt.h>
#include <util/delay.h>
#define SH_CP 0x04
#define DS PD3
#define ST_CP PD4
#define R1 PD5
#define R2 PD6
#define k1 PC0
#define k2 PC1
#define k3 PC2
#define k4 PC3
#define KNC PB3
#define Reset PB0
#define Up PB1
#define Dwn PB2
uint8_t numArray[] = {
	0b01111110,  //0
	0b00110000,  //1
	0b01101101,  //2
	0b01111001,  //3
	0b00110011,  //4
	0b01011011,  //5
	0b01011111,  //6
	0b01110000,  //7
	0b01111111,  //8
	0b01111011,  //9
	0b00000000   //All clear
};
uint8_t clkNum = 0, digitNum = 0;
volatile uint16_t num = 1000;
int digData[4] = {
	6,  //0
	5,  //1
	4,  //2
	3,  //3
};
void reverseDigits(int num) {
	if (num > 799 && num < 10000) {
		int i;
		for (i = 0; i < 4; i++) {
			digData[i] = num % 10;
			num /= 10;
		}
	}
}
void ComeOnBabyLightMyFire(){
	PORTD |= (1 << PD5);  // Включаем диод на PD5
	_delay_ms(9999);
	PORTD &= ~(1 << PD5);  // Выключаем диод на PD5
}
void setup() {
	// Настройка портов ввода-вывода
	DDRD |= (1 << PD2) | (1 << PD3) | (1 << PD4) | (1 << PD5) | (1 << PD6);
	DDRC |= (1 << k1) | (1 << k2) | (1 << k3) | (1 << k4);
	DDRB |= (1 << PB0) | (1 << PB1) | (1 << PB2) | (1 << PB3);  // Устанавливаем пины кнопок порта B на выход
	PORTC |= (1 << k1) | (1 << k2) | (1 << k3) | (1 << k4);
	PORTD &= ~(1 << PD5);  // Диод выключен
	// Включение прерывания INT1 по спаду сигнала на PB3
	EICRA |= (1 << ISC10) | (1 << ISC11);  // Прерывание INT1 по спаду и фронту сигнала на PB3
	EIMSK |= (1 << INT1);  // Разрешение прерывания INT1
	sei();                               // Глобальное разрешение прерываний
	num = 800;
	reverseDigits(num);
}
void loop() {
	PORTD ^= SH_CP;
	if (!(PORTD & SH_CP)) {
		if (numArray[digData[digitNum]] & (1 << clkNum)) PORTD |= (1 << PD3);
		else PORTD &= ~(1 << PD3);
		if (clkNum < 7) clkNum++;
		else {
			PORTD |= 0x10;
			// delayMicroseconds(4);  // Закомментировал задержку, так как она может быть неточной
			PORTD &= ~0x10;
			PORTC = (PORTC & 0xF0) | (0x08 >> digitNum);
			clkNum = 0;
			if (digitNum < 3) digitNum++;
			else digitNum = 0;
			if (TCCR1B & (1 << CS10)) {
				if (TCNT1 >= (num * 16)) {
					PORTD &= ~(1 << PD5);  // Выключаем диод на PD5
					TCCR1B &= ~((1 << CS10) | (1 << CS11));  // Остановка таймера
					TCNT1 = 0;            // Сброс значения счетчика таймера
				}
			}
		}
	}
}
void buttonCheck() {
	if ((PINB & (1 << PB3))) {
		ComeOnBabyLightMyFire();
	}	
	if (PINB & (1 << PB1)) {
		num = 800;
		reverseDigits(num);
		if ((TCCR1B & (1 << CS10)) == 0) {  // Проверка, что таймер остановлен
			OCR1A = num * 16;  // Обновление значения сравнения в таймере
		}
	}
	if (PINB & (1 << PB0)) {
		if (num < 10000) {
			num++;
			reverseDigits(num);
			if ((TCCR1B & (1 << CS10)) == 0) {  // Проверка, что таймер остановлен
				OCR1A = num * 16;  // Обновление значения сравнения в таймере
			}
		}
	}
	if (PINB & (1 << PB2)) {
		if (num > 799) {
			num--;
			reverseDigits(num);
			if ((TCCR1B & (1 << CS10)) == 0) {  // Проверка, что таймер остановлен
				OCR1A = num * 16;  // Обновление значения сравнения в таймере
			}
		}
	}
}
// Прерывание INT1
ISR(INT1_vect) {
	buttonCheck();
}
int main() {
	setup();
	while (1) {
		loop();
	}
	return 0;
}