Привет самоделкины! В данной статье вашему вниманию предлагается небольшой приборчик, который поможет отсчитать нужное время во многих сферах вашей деятельности. Конечно, сейчас имеется огромное количество подобных приспособлений. даже в наших смартфонах есть подобное приложение. Но намного приятней изготовить самому и использовать его в быту. Что бы он был всегда под рукой и его легко можно было бы использовать.
В данном проекте в качестве считывателя использован механический энкодер. Конкретно модель EC12E24204A8 с высотой ручки 12мм.
Именно такой элемент больше всего подходит для реализации проекта. Под его контакты на монтажной плате имеются отверстия. И рассчитано положение для свободной шестеренчатой передачи внутри корпуса для изменения значений на цифровом дисплее.
Посмотреть ознакомительное видео с работой готового устройства можно на канале YouTube. Как можно заметить - градация отсчета и индикации составляет 1 секунду, а управление сведено к минимуму, что очень удобно.
Что касается схемы приборчика, то там сложного ничего нет. Как и писалось выше основным инструментом для считывания поворота части корпуса и задания времени срабатывания сигнала зуммера используется механический энкодер. Но все таки основным элементом является программируемый микроконтроллер ATtiny3216. У него имеется достаточное количество контактов, что бы использовать их для подключения напрямую к дисплею, не используя шину I2C. Это довольно таки распространенный микроконтроллер, поэтому найти его не составит труда. Если посмотреть на схему, станет понятным использование микросхемы 74HC14. В спящем режиме она блокирует контакты энкодера для экономии расхода заряда источника питания.
После распечатки деталей корпуса (ссылка для скачивания будет в конце статьи) и травления платы (так же оригинальная плата будет предоставлена для скачивания). можно приступать к сборке прибора.
Плата для монтажа будет выглядеть вот так:
Начать электромонтаж лучше с мелких деталей. Первым делом установите SMD компоненты микросхему 74HC14 и микроконтроллер ATtiny3216.
Дальше можно припаять пару разъемов. Это будет разъем для программатора и разъем для батарейки. Можно установить кнопку с обратной стороны платы.
Дальше можно установить индикатор со стороны где установлена кнопка.
Почти последней припаянной деталькой будет поворотный энкодер. Он устанавливается со стороны, где расположены микросхемы. Пропаивать деталь не спешите, сначала примерьте. Проверьте как будет сидеть на месте и в корпусе. Все таки в дальнейшем на него будет механическое воздействие.
Со стороны индикатора и кнопки установите зуммер. Вот теперь смонтированы все радиокомпоненты!
Начинаем сборку. Первым делом нужно установить распечатанную на принтере шестеренку (в скачанных файлах будетTwist - Pinion) на поворотный вал энкодера. Зафиксировать можно с помощью клея.
Готовую плату нужно вставить в верхнюю часть корпуса (Twist - Top). Закрепить ее можно с помощью пары винтиков М3.
Дальше распечатанные на принтере детали нужно скомпоновать между собой. Сначала надо склеить шестерню с внутренними зубьями (Twist - Ring) и внешний обод (Twist - Rim).
Теперь важная часть сборки прибора. При установке на место сборки деталей с внутренней шестерней на сборку верхней части и платы, важно обратить внимание на свободный ход (поворот) большой шестерни относительно шестеренки, закрепленной на валу энкодера. Усилий не должно быть никаких и вращение малой шестеренки должно быть легким и свободным.
Следующим шагом к нижней части корпуса (Twist - Bottom) нужно установить контакты в батарейном отсеке и припаять к ним провода питания с установленным встречным разъемом что был припаян к плате.
Если вращение частей корпуса и поворот вала энкодера свободные, без "затираний" и тем более усилий, можно приступить к следующей части. А именно программирование микроконтроллера. В случае если механическому движению частей что мешает, воспользуйтесь надфилями и припИлите проблемные зоны.
Для заливки кода в микроконтроллер вам понадобится UPDI программатор. О его постройке самому на основе Arduino есть множество статей в свободном доступе нашего интернета. Имеются и готовые решения, поэтому на этом вопросе останавливаться долго не стоит.
Для проверки работоспособности прибора осталось вставить пару батареек в отсек и включить прибор.
New mechanics, electronics and software for "an Attiny85 Twist-to-Set Kitchen Timer" by bobson.h (https://www.instructables.com/an-Attiny85-Twist-to-Set-Kitchen-Timer/)
2027-04-20 V1 John Bradnam (jbrad2089@gmail.com) - Replaced optical rotary encoder with a mechanical rotary encoder - Designed new hardware around ATtiny3216 microprocessor - Initial code base
#define TCB_COMPARE 150 //Refresh value for 7 segment display (1000@20MHz, 100@4MHZ) #define BRIGHTNESS 2 //Initial brightness level (0 to 15) #define DIGITS 4 //Number of digits in display
uint8_t digits[DIGITS]; //Holds the current digits to display uint8_t nextDigit = 0; //Holds next digit to display uint8_t brightness = BRIGHTNESS; //Current Brightness level (0 to 15) uint8_t bamCounter = 0; //Bit Angle Modulation variable to keep track of things uint8_t bamBit; //Used to store bit to test against brightness value
#define COLON_FLASH_RATE 500; unsigned long colonTimeout; bool colonOn = false;
#define SLEEP_TIMEOUT 10000 //Amount of time no switch is pressed before going to sleep uint32_t sleepTimeout = 0; //Used to timeout mode switch function to sleep function
//----------------------------------------------------------------------------------- //Display a number on the 7 segment display void displayTime(int secs, bool colon) { displayNumber(secs / 60, colon, true, 2); displayNumber(secs % 60, colon, true, 0); }
//----------------------------------------------------------------------------------- //Display a number on the 7 segment display void displayNumber(int number, bool colon, bool leading, int pos) { for (uint8_t i = 0; i < 2; i++) { if (number > 0 || leading || i == 0) { digits[pos + i] = number % 10; } else { digits[pos + i] = SPACE; } if (colon && pos == 2 && i == 0) { digits[pos + i] |= 0x80; } number = number / 10; } }
//----------------------------------------------------------------------------------- //Turn off display void displayOff() { //Turn off display timer TCB1.CTRLA = TCB1.CTRLA & ~TCB_ENABLE_bm;
#define NUM_NOTES 5 const int closeEncounters[] PROGMEM = { // notes in the melody: NOTE_A2, NOTE_B2, NOTE_G2, NOTE_G1, NOTE_D2 // "Close Encounters" tones }; #define NOTE_DURATION 800
//Play the "Close Encounters" melody void soundAlaram() { detachRotaryPinChangeInterrupt(); int thisNote; alarmOn = true; while (alarmOn) { displayOff(); thisNote = 0; while (alarmOn && (thisNote < NUM_NOTES)) { tone(SPEAKER, pgm_read_byte(closeEncounters + thisNote) * 4); delay(NOTE_DURATION); thisNote++; } noTone(SPEAKER); displayOn(); long timeout = millis() + 2000; while (alarmOn && (millis() < timeout)) { yield(); } } attachRotaryPinChangeInterrupt(); ignoreNextPress = true; }
//-------------------------------------------------------------------- //Write the EepromData structure to EEPROM void writeEepromData(void) { //This function uses EEPROM.update() to perform the write, so does not rewrites the value if it didn't change. EEPROM.put(EEPROM_ADDRESS,EepromData); }
//-------------------------------------------------------------------- //Read the EepromData structure from EEPROM, initialise if necessary void readEepromData(void) { #ifndef RESET_EEPROM EEPROM.get(EEPROM_ADDRESS,EepromData); if (EepromData.magic != EEPROM_MAGIC) { #endif EepromData.magic = EEPROM_MAGIC; EepromData.countdownTime = 0; writeEepromData(); #ifndef RESET_EEPROM } #endif }
//-------------------------------------------------------- // Shut down display and put ATtiny to sleep // Will wake up when SWITCH is pressed void gotoSleep() { displayOff(); detachRotaryPinChangeInterrupt(); set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode is set here sleep_enable(); sleep_mode(); // System actually sleeps here sleep_disable(); // System continues execution here when watchdog timed out attachRotaryPinChangeInterrupt(); displayOn(); timeChanged = true; // Force a screen update sleepTimeout = millis() + SLEEP_TIMEOUT; ignoreNextPress = true; }
//------------------------------------------------------------------------- //Timer B Interrupt handler interrupt each mS - output segments ISR(TCB1_INT_vect) {