Простой кухонный таймер на PIC16F84. Моя реализация » Журнал практической электроники Датагор

⚡️таймер времени для кухни |

На страницах журнала “Радио” неоднократно публиковались различные конструкции таймеров, собранных на цифровых микросхемах структуры КМОП. Эти таймеры имеют возможность изменения выдержки времени в широких пределах и поэтому достаточно универсальны.

Надежный таймер реального времени на кухнеОднако применительно к таймеру, предназначенному для эксплуатации хозяйкой на кухне, эта универсальность может сыграть злую шутку.

Использование нескольких переключателей для выбора интервала выдержки может привести к ошибкам при установке времени, а наличие отдельных сетевого выключателя и кнопки запуска явно не способствует удобству эксплуатации таймера.

К тому же таймер времени желательно снабдить индикатором, позволяющим судить о времени, оставшемся до окончания выдержки. Предлагаемый вниманию читателей кухонный таймер времени имеет 11 фиксированных значений выдержки времени, наиболее употребительных (по мнению автора) в процессе приготовления пищи. При желании длительность выдержки можно изменить, уменьшив или увеличив их число за счёт изменения коммутации счётчиков микросхем, а также применив переключатель на другое число положений.

Таймер для кухни не имеет обособленного выключателя питания, его включение и выключение производятся с помощью одной кнопки. По окончании выдержки времени звучит звуковой сигнал, после чего таймер времени вместе с нагрузкой отключается от сети. Таймер времени электронный снабжён наглядным линейным индикатором времени, позволяющим даже при беглом взгляде на прибор оценить время, как прошедшее с момента пуска, так и оставшееся до окончания выдержки.

С помощью таймера можно управлять нагрузкой, подключённой к нему либо непосредственно, либо через дополнительный коммутатор. Недостатком предлагаемой конструкции является невозможность получения некоторых интервалов выдержек времени (например, 15 и 50 мин) вследствие выбранного способа коммутации счётчиков микросхем, а также отсутствие резервного источника питания, что может привести к сбою установленного интервала времени и отключению устройства при кратковременных перерывах в подаче сетевого напряжения.

Схема устройства показана на сайте www.kuhonnij.ru см. рис. 1. При нажатии на кнопку SB1 на таймер времени и подключённую к нему нагрузку поступает сетевое напряжение, при этом срабатывает реле времени К1 и своими контактами К1.1 блокирует контакты кнопки. В момент подачи напряжения питания цепь C8R15 формирует импульс сброса, переводящий счетчики всех микросхем в нулевое состояние. На микросхеме К176ИЕ18 (DD1), включённой по стандартной схеме, собран генератор секундных и минутных импульсов.

Секундные импульсы с выхода генератора поступают на вход счётчика DD2.1, с выхода которого можно снять импульсы с периодом следования 2, 4, 8 и 16 с. С помощью сдвоенного переключателя SA1 осуществляют установку времени выдержки. Счётный вход счётчика DD3 через переключатель SA1.1 может быть соединён с одним из выходов счётчика DD2.1 либо с “минутным” выходом М микросхемы DD1. С помощью переключателя SA1.2 осуществляется изменение коэффициента деления счетчика DD3 2, 3, 4, 5, 6 и 8.

Импульсы с частотой, поделённой на соответствующий коэффициент, снимаются с выхода 0 счётчика DD3 и через диод VD12 поступают на вход счётчика DD2.2, к выходам которого подключены диоды VD17— VD20, выполняющие функцию элемента И. При достижении на выходах счётчика DD2.2 состояния 1111 на анодах этих диодов благодаря резистору R14 появляется высокий логический уровень, который через диод VD13 блокирует работу счётчика DD2.2 и открывает полевой транзистор VT2, который, в свою очередь, включает звуковой сигнал — пьезосирену НА1 со встроенным генератором.

На транзисторе VT1 и тринисторе VS1 выполнен узел автоматического отключения таймера от сети спустя минуту после появления звукового сигнала. По фронту каждого минутного импульса, появляющегося на выводе 10 микросхемы DD1, конденсатор С2 заряжается через резистор R6, открывая на короткое время транзистор VT1. Пока на коллекторе этого транзистора присутствует напряжение низкого уровня, тринистор VS1 закрыт. По окончании выдержки таймера по фронту последнего минутного импульса конденсатор С2 заряжается раньше, чем происходит переключение счётчиков микросхем DD2 и DD3, поэтому ко времени появления на коллекторе транзистора VT1 напряжения высокого уровня транзистор оказывается закрытым.

По фронту следующего минутного импульса транзистор VT1 вновь открывается, но теперь на его коллекторе присутствует напряжение высокого уровня, открывающее тринистор VS1. Открывшийся тринистор шунтирует обмотку реле К1, контакты К1.1 реле размыкаются, и таймер времени отключается от сети. Продолжительность звукового сигнала до момента отключения таймера при установке выдержки 20 мин и более — 1 мин, при выдержке менее 20 мин — немного меньше.

Для ручного отключения таймера нажимают на кнопку SB1, при этом конденсатор С9, заряженный до напряжения питания, контактами SB1.2 подключается к управляющему электроду тринистора, вызывая отключение реле К1. В момент отпускания кнопки SB1 таймер отключается, конденсатор С9 разряжается через цепь питания таймера и устройство готово к повторному пуску. Узел индикации времени, прошедшего с момента пуска таймера, построен с применением линейного газоразрядного индикатора ИН-9 [4]. Резисторы R16—R19 образуют цифроаналоговый преобразователь, преобразующий цифровой код на выходах счётчика DD2.2 в напряжение.

С движка подстроечного резистора R21 напряжение, пропорциональное прошедшему с момента пуска таймера времени, поступает на базу транзистора VT3, выполняющего функцию регулируемого резистора [5]. С увеличением напряжения на базе транзистора VT3 возрастает ток, текущий через индикатор HL2, и соответственно, увеличивается высота светящегося столба индикатора. Линейность шкалы индикатора зависит от точности подбора резисторов R16—R19, но так как индикатор служит больше для контроля, а не для отсчёта времени, добиваться высокой линейности шкалы не имеет смысла.

Чертёж печатной платы таймера показан на рис. 2. Она изготовлена из двухстороннего фольгированного стеклотекстолита толщиной 2 мм. Чертёж размещения элементов на плате показан на рис. 3, а внешний вид смонтированной платы — на рис. 4. Выводы деталей, соединяющие печатные проводники на разных сторонах платы, необходимо пропаивать с двух сторон. Для облегчения изменения времени выдержки таймера к контактным площадкам, служащим для присоединения проводов от переключателя SA1, подключены все, в том числе и не задействованные в данной конструкции, выходы счётчиков микросхем DD2.1 и DD3.

Резистор R10 установлен на плате параллельно, резистор R11 — перпендикулярно ей, при этом один из его выводов припаян к свободному выводу резистора R10, а другой — к выводу катода стабилитрона VD15. Конденсаторы СЗ и С4, а также микросхема DA1 установлены на плате “лёжа”. Над микросхемой DD3 на металлических стойках высотой 5 мм установлена пластина из оцинкованного железа толщиной 0,5 мм, оклеенная в два слоя полосками изоляционной ленты. Края пластины загнуты вверх, образуя коробчатое основание, в котором установлен трансформатор Т1.

Пластина с помощью изолированного отрезка провода соединена с общим проводом устройства. Держатель предохранителя FU1 и клеммная колодка, служащая для присоединения проводов, идущих к вилке ХР1 и гнезду XS1, смонтированы на небольшой пластине из полистирола, которая закреплена на стойках высотой 20 мм над реле К1. Индикатор HL2, светодиод HL1, а также кнопка SB1 и переключатель SA1 смонтированы на пластине из полистирола, имеющей размеры печатной платы. Эта пластина закреплена над платой с помощью металлических стоек высотой 40 мм, имеющих резьбу М3.

Звуковой излучатель с платой генератора извлечены из корпуса пьезосирены. Излучатель закреплён с помощью стойки и длинного винта рядом с трансформатором Т1, плата звукового генератора прикреплена к основной плате с помощью металлического уголка. Звуковой сигнализатор таймера можно выполнить без использования пьезосирены со встроенным генератором, задействовав элементы звукового сигнализатора микросхемы DD1.

На схеме (см. рис. 1) элементы этого варианта выделены красным цветом, а обозначение начинается с префикса 1. Транзистор 1VT1 служит для предотвращения включения звукового сигнала в первую минуту после пуска таймера, стабилитрон 1VD1 препятствует появлению даже слабого звука в телефоне 1BF1 в первую минуту работы таймера. При использовании этого варианта звукового сигнализатора транзистор VT2, резистор R7 и излучатель НА1 не устанавливают.

Читайте также:  Светодиодная подсветка стеклянных полок: инструкция по установке

Плата рассчитана на монтаж звукового сигнализатора как по первому, так и по второму варианту. Для реализации второго варианта на плату монтируют элементы 1VD1, 1VD2, 1VT1. Корпусом конструкции служит подходящий по размерам контейнер для пищевых продуктов (рис. 5), изготовленный из довольно толстой жёсткой пластмассы и имеющий полупрозрачное дно, через которое можно наблюдать свечение индикатора HL2 и вспышки светодиода HL1.

Плата закреплена на крышке контейнера, служащей днищем, на стойках высотой 5 мм с помощью винтов М3. С обратной стороны днища прикреплена металлическая пластина с грушевидным вырезом, служащая для подвески таймера на держателе, закреплённом на стене. В устройстве можно применить резисторы МЛТ, С2-23, причём каждый из резисторов R16—R19 для облегчения подбора можно составить из двух, соединённых последовательно, установив их перпендикулярно плате, подстроечный резистор — СП4-1, СПО.

Оксидные конденсаторы — К50-35 или импортные, остальные — керамические КМ, К10-7в или аналогичные. Транзистор КТ3102А можно заменить любым из серий КТ3102, КТ315, замена транзистора КТ940Б — транзистор КТ969А или аналогичный средней мощности с допустимым напряжением коллектор- эмиттер не менее 200 В. Хотя транзистор VT3 нагревается не очень сильно, для большей надёжности его можно снабдить небольшим теплоотводом. Полевой транзистор можно применить практически любой n-канальный с индуцированным каналом и с максимальным током стока не менее 0,3 А.

Диоды VD6—VD9 — любые выпрямительные с обратным напряжением не менее 50 В и допустимым током не менее 0,5 А, например, любой из серии 1N400x. Диоды VD2—VD5 должны иметь обратное напряжение не менее 400 В (1N4004, 1N4005, 1N4006, 1N4007), остальные диоды — серий КД521, КД522, 1N4148. Трансформатор Т1 применён готовый тороидальный, но подойдёт любой другой с напряжением вторичной обмотки 15…20В при токе до 0,5 А.

Желательно применять трансформатор, имеющий дополнительную вторичную обмотку с напряжением 180…220В для питания выпрямителя индикатора на диодах VD2-VD5, при этом элементы устройства будут гальванически изолированы по питающей сети. Микросхему К142ЕН8А можно заменить на 7809, микросхемы DD2, DD3 — функциональными аналогами из серий К176, КР1561. Взамен микросхемы К176ИЕ18 можно применить микросхему К176ИЕ12, отогнув перед установкой на плату выводы 9 и 14 и предварительно соединив отрезками проводов вывод 9 с выводом 5, а вывод 7 — с выводом 4.

Но при такой замене звуковую сигнализацию придётся делать с применением элементов R7, VT2 и НА1. Пьезосирена НА1 — неизвестного происхождения, без единой надписи на корпусе. При напряжении питания 9В сирена потребляет ток 250 мА и звучит очень громко. В качестве НА1 можно использовать и другие звуковые излучатели с требуемой громкостью сигнала, имеющие напряжение питания в пределах 9… 12 В. BF1 — капсюльТК-67, или динамическая головка с сопротивлением катушки не менее 50 Ом, на-пример, 0.025ГД-2 или 0.05ГД-1.

Стабилитрон 1VD1 — не обязательно двух-анодный, подойдёт любой маломощный стабилитрон с напряжением стабилизации 5…7 В, подключённый анодом к базе транзистора 1VT1. Стабилитроны VD15, VD16 можно заменить одним КС650А или цепочкой стабилитронов с суммарным напряжением стабилизации 150—160 В. Тринистор VS1 — КУ101 с любым буквенным индексом. Светодиод НИ должен быть повышенной яркости свечения, любого типа и свечения. Линейный газоразрядный индикатор ИН-9 можно заменить индикатором ИН-13, включив его по схеме, приведённой на рис. 1 в [6].

При отсутствии газоразрядного индикатора линейную шкалу можно построить на светодиодах с применением микросхемы К1003ПП1 [6]; при этом отпадает необходимость в высоковольтном источнике питания. Кнопка SB 1 — КМ2-1, П2К или другая без фиксации, имеющая две группы контактов на переключение. Переключатель SA1 —- ПГЗ на два положения и 11 направлений, можно применить и любой малогабаритный на два направления и требуемое число положений. Реле — SANOU SRD-S-112 D с напряжением срабатывания 12 В и коммутируемым током 7 А извлечено из неисправного источника бесперебойного питания IPPON (из одного источника можно извлечь пять таких реле).

Подойдёт также и любое другое реле, имеющее напряжение срабатывания 10… 15В при токе 30…40 мА. В качестве вилки ХР1 использован корпус от зарядного устройства сотового телефона или другого малогабаритного блока питания, выполненный в виде сетевой вилки. В этом корпусе смонтирована и розетка XS1, служащая для подключения управляемой таймером нагрузки.

Номинальный ток плавкой вставки FU1 намеренно выбран значительно меньше максимально допустимого для контактов кнопки SB1 тока, что позволяет значительно продлить ресурс контактов кнопки и предохранить их от обгорания при ошибочном подключении к таймеру мощной нагрузки. Для коммутации нагрузки мощностью более 200 Вт к розетке XS1 таймера следует подключить дополнительный мощный релейный коммутатор или вход управления симисторного коммутатора.

Налаживание “часовой” и индикаторной частей устройства лучше производить по отдельности. Диоды VD2— VD5 на первом этапе налаживания на плату не устанавливают. После включения питания и нажатия на кнопку SB1 реле К1 должно сработать, а светодиод HL1 должен начать мигать с частотой 0,5 Гц, свидетельствуя о нормальной работе генератора на микросхеме DD1. Далее измеряют напряжение на обмотке реле К1. При необходимости подбирают резистор R13, устанавливая на обмотке номинальное для применённого реле напряжение.

Затем повторно нажимают на кнопку SB1, при отпускании кнопки устройство должно отключиться от сети. Устанавливают переключателем SA1 интервал 3 мин и, контролируя время с помощью часов или секундомера, вновь включают таймер времени кнопкой SB1. По истечении трёх минут должен зазвучать звуковой сигнал, а спустя ещё 39 с таймер должен отключиться. Если всё в порядке, проверяют работу таймера на других выдержках. В случае, если при выдержке 30 мин и более таймер по её окончании сразу, без подачи звукового сигнала, отключается от сети, подбирают сопротивление резистора R6 или ёмкость конденсатора С2.

Убедившись в нормальной работе “часовой” части таймера, впаивают диоды VD2—VD5, устанавливают движок подстроечного резистора R21 в среднее положение и включают таймер времени на интервал 3 мин. Дождавшись появления звукового сигнала, подстроечным резистором R21 устанавливают максимальную длину светящегося столба индикатора. Далее устройство вновь включают на интервал 3 мин, и по секундомеру определяют время, через которое на индикаторе появляется первая часть светящегося столба.

Это время, в зависимости от желаемой длины шагов, может составлять 12 или 24 с (в авторском варианте — 24 с). Если это время больше, уменьшают сопротивление резистора R22, подстроечным резистором R21 во время звучания сигнала вновь устанавливают максимальную высоту светящегося столба индикатора и снова измеряют время появления первого светящегося сегмента. Если оно соответствует 12 или 24 с, налаживание можно считать законченным.

Остаётся расположить рядом с индикатором полоску бумаги, отметить на ней все шаги, которые делает индикатор за время выдержки, и изготовить шкалу, обозначив нужные точки на ней в процентах от времени выдержки. При изготовлении и эксплуатации данного устройства необходимо помнить, что все его элементы гальванически связаны с сетью. Корпус таймера следует изготавливать только из изоляционного материала.

Необходимо исключить соприкосновение с корпусом и валом переключателя SA1, применив надёжную рукоятку из изоляционного материала. Винт, фиксирующий ручку переключателя, должен быть обязательно утоплен вглубь ручки для исключения касания этого винта во время эксплуатации устройства. Для устранения возможности поражения электрическим током при налаживании устройство следует питать через разделительный трансформатор.

Динамическая индикация

Настройка динамической индикации не
очень сложная. Для удобства, необходимо
сделать несколько дефайнов:

  1. #define ON1 GPIO_WriteHigh(GPIOC,GPIO_PIN_3)

  2. #define ON2 GPIO_WriteHigh(GPIOD,GPIO_PIN_5)

  3. #define ON3 GPIO_WriteHigh(GPIOD,GPIO_PIN_6)

  4. #define OFF1 GPIO_WriteLow(GPIOC,GPIO_PIN_3)

  5. #define OFF2 GPIO_WriteLow(GPIOD,GPIO_PIN_5)

  6. #define OFF3 GPIO_WriteLow(GPIOD,GPIO_PIN_6)

  7. #define AON GPIO_WriteLow(GPIOB,GPIO_PIN_4)

  8. #define BON GPIO_WriteLow(GPIOA,GPIO_PIN_1)

  9. #define CON GPIO_WriteLow(GPIOC,GPIO_PIN_6)

  10. #define DON GPIO_WriteLow(GPIOD,GPIO_PIN_3)

  11. #define EON GPIO_WriteLow(GPIOD,GPIO_PIN_2)

  12. #define FON GPIO_WriteLow(GPIOC,GPIO_PIN_4)

  13. #define GON GPIO_WriteLow(GPIOC,GPIO_PIN_5)

  14. #define TON GPIO_WriteLow(GPIOC,GPIO_PIN_7)

  15. #define ALLOFFGPIO_WriteHigh(GPIOC,GPIO_PIN_6|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_7);GPIO_WriteHigh(GPIOD,GPIO_PIN_2|GPIO_PIN_3);GPIO_WriteHigh(GPIOA,GPIO_PIN_1|GPIO_PIN_3);GPIO_WriteHigh(GPIOB,GPIO_PIN_4);

ONx.. OFFx — позволяют включить и выключить
1 цифру. AON..TON — один сегмент в цифре.
ALLOFF — выключить все сегменты. Далее,
нам нужна вспомогательная функция —
cifraS(), которая будет отображать в одной
цифре нужный сегмент для выбранного
символа (например буквы L, прочерка и
т. д.).

  1. void cifraS(u8 seg){

  2. u8 j=;

  3. if(seg==) AON;

  4. if(seg==1) BON;

  5. if(seg==2) CON;

  6. if(seg==3) DON;

  7. if(seg==4) EON;

  8. if(seg==5) FON;

  9. if(seg==6) GON;

  10. if(seg==7) TON;

  11. for(j=;j<3;j ){

  12. u8 num;

  13. u8 flag=;

  14. if(seg==7){

  15. if(tchk[j]* tchkmig) flag =1;

  16. }else{

  17. num = ind[j];

  18. if(num==){

  19. //AON;BON;CON;DON;EON;FON;

  20. if(seg==) flag=1;

  21. if(seg==1) flag=1;

  22. if(seg==2) flag=1;

  23. if(seg==3) flag=1;

  24. if(seg==4) flag=1;

  25. if(seg==5) flag=1;

  26. }

  27. if(num==1){

  28. //BON;CON;

  29. if(seg==1) flag=1;

  30. if(seg==2) flag=1;

  31. }

  32. if(num==2){

  33. //AON;BON;GON;EON;DON;

  34. if(seg==) flag=1;

  35. if(seg==1) flag=1;

  36. if(seg==3) flag=1;

  37. if(seg==4) flag=1;

  38. if(seg==6) flag=1;

  39. }

  40. if(num==3){

  41. //AON;BON;CON;DON;GON;

  42. if(seg==) flag=1;

  43. if(seg==1) flag=1;

  44. if(seg==2) flag=1;

  45. if(seg==3) flag=1;

  46. if(seg==6) flag=1;

  47. }

  48. if(num==4){

  49. //FON;GON;BON;CON;

  50. if(seg==1) flag=1;

  51. if(seg==2) flag=1;

  52. if(seg==5) flag=1;

  53. if(seg==6) flag=1;

  54. }

  55. if(num==5){

  56. //AON;FON;GON;CON;DON;

  57. if(seg==) flag=1;

  58. if(seg==2) flag=1;

  59. if(seg==3) flag=1;

  60. if(seg==5) flag=1;

  61. if(seg==6) flag=1;

  62. }

  63. if(num==6){

  64. //AON;EON;FON;GON;CON;DON;

  65. if(seg==) flag=1;

  66. if(seg==2) flag=1;

  67. if(seg==3) flag=1;

  68. if(seg==4) flag=1;

  69. if(seg==5) flag=1;

  70. if(seg==6) flag=1;

  71. }

  72. if(num==7){

  73. //AON;BON;CON;

  74. if(seg==) flag=1;

  75. if(seg==1) flag=1;

  76. if(seg==2) flag=1;

  77. }

  78. if(num==8){

  79. //AON;BON;CON;GON;DON;EON;FON;

  80. if(seg==) flag=1;

  81. if(seg==1) flag=1;

  82. if(seg==2) flag=1;

  83. if(seg==3) flag=1;

  84. if(seg==4) flag=1;

  85. if(seg==5) flag=1;

  86. if(seg==6) flag=1;

  87. }

  88. if(num==9){

  89. //AON;BON;CON;GON;DON;FON;

  90. if(seg==) flag=1;

  91. if(seg==1) flag=1;

  92. if(seg==2) flag=1;

  93. if(seg==3) flag=1;

  94. if(seg==5) flag=1;

  95. if(seg==6) flag=1;

  96. }

  97. if(num==14){

  98. //-

  99. if(seg==6) flag=1;

  100. }

  101. if(num==11){

  102. //L

  103. if(seg==3) flag=1;

  104. if(seg==4) flag=1;

  105. if(seg==5) flag=1;

  106. }

  107. if(num==12){

  108. //K

  109. if(seg==) flag=1;

  110. if(seg==3) flag=1;

  111. if(seg==4) flag=1;

  112. if(seg==5) flag=1;

  113. }

  114. if(num==15){

  115. //K

  116. if(seg==) flag=1;

  117. if(seg==3) flag=1;

  118. if(seg==4) flag=1;

  119. if(seg==5) flag=1;

  120. }

  121. }

  122. if(flag==1&& j==) ON1;

  123. if(flag==1&& j==1) ON2;

  124. if(flag==1&& j==2) ON3;

  125. }

  126. }

С дефайнами она выглядит симпатично.
Активируем нужный сегмент и проверем
в каких из трех цифр он активен. Там где
активен — включаем общий анод. Если вам
нужны другие символы на индикаторе, то
надо добавить еще условия, например,
(num==11) — вывести на индикатор L.

Читайте также:  Освещение в гостиной: Варианты и идеи дизайна — Свет в гостиной комнате, как сочетать люстры и светильники | Houzz Россия

Для того, чтобы вывести что-то на
индикатор, достаточно положить в массивы
ind[] и tchk[] нужные нам значения, остальное
будет сделано в прерываниях. Вот так
можно вывести число от 0 до 999.

  1. void shownumber(u16 num){

  2. ind[]=num/100;

  3. ind[1]=num/10-ind[]*10;

  4. ind[2]=num - ind[]*100- ind[1]*10;

  5. }

Теперь, собственно сам вывод на
индикатор. Вся работа ведётся в обработчике
прерывания TIM2. Введём переменную numind,
которая будет обозначать, какой сегмент
показывать в данный вызов прерывания.

  1. INTERRUPT_HANDLER(TIM2_UPD_OVF_BRK_IRQHandler,13)

  2. {

  3. TIM2_ClearITPendingBit(TIM2_IT_UPDATE);

  4. OFF1;

  5. OFF2;

  6. OFF3;

  7. ALLOFF

  8. if(numind<8){

  9. cifraS(numind);

  10. }

  11. numind ;

  12. if(numind==16) numind=;

  13. }

Очищаем бит контроля обработки
прерывания. Далее выключаем все разряды.
Выводим нужные сегменты по всех трех
цифрах. А потом несколько раз ничего не
выводим. Пропуски нужны для регулирования
яркости. У нас получится такая картина

Первый Сегмент, Второй Сегмент, Третий
Сегмент, Ничего не горит …. Ничего не
горит, Первый сегмент и т.д….

и так, очень быстро. Чем больше мы
выводим пустых разрядов — тем более
тускло светится индикатор, и меньше
тратит энергии.

Обработка кнопок

В кухонном таймере всего две кнопки.
Нам надо нагрузить на них максимальный
функционал. Поэтому будет нужна обработка
одиночного, двойного и долгого нажатия
кнопки. Напишем функцию которая сразу
определит какой вид нажатия был и вернёт
это в основной цикл. Там вы сможете
обработать это как вам надо.

Для настройки процедуры обработки
кнопок есть несколько дефайнов:

  1. #define KNNUM 2 //колво кнопок

  2. #define KNONE 4 //обычное нажатие

  3. #define KNLONG 5 //долгое

  4. #define KNTWO 6 //двойное

  5. #define KNDTIME 50 //время в мс дребезга контактов

  6. #define KNTIMETWO 300 //время в мс двойногонажатия

  7. #define KNTIMELONG 1000 //время в мс долгогонажатия

Количество кнопок, значения, которые
возвращает функция определения кнопок,
время необходимое для устранения
дребезга, время в течение которого
должно произойти двойное нажатие, время
фиксации долгого нажатия. Для хранения
обработанных данных используется массив
kn[], для хранения предыдущего состояния
кнопок массив knstatus[], времени, прошедшего
с начала нажатия первой кнопки —
kntime[]. При инициализации МК, необходимо
очистить все эти массивы.

Сама процедура обработки кнопок
вызывается из миллисекундного таймера
каждые 50 мс (время нужное для устранения
дребезга), так сразу решается проблема
с дребезгом контактов.

  1. u8 i,pd;

  2. if(kndtime) kndtime--;

  3. if(kndtime==){

  4. //опрос кнопок редко для избежаниядребезга

  5. kndtime = KNDTIME;

  6. pa = GPIOA->IDR;//GPIO_ReadInputData(GPIOA);

  7. pb = ~(GPIOB->IDR);//_ReadInputData(GPIOB);

  8. knint(, pa, GPIO_PIN_2);

  9. knint(1, pb, GPIO_PIN_5);

  10. }

  11. for(i=;i<KNNUM;i )if(kntime[i]) kntime[i]--;

Предварительно считываем состояние
порта, на котором висят кнопки. Далее,
передаем это значение, номер кнопки,
номер вывода кнопки в функцию, в которой
будет вся обработка — knint(). Так как
кнопка на порту B нажата когда там будет
значение 0, то используем операцию
инверсии битов.

В конце уменьшаем на единицу переменные
таймеры кнопок.

Рассмотрим саму функцию обработки
кнопок.

  1. if(knstatus[knum]&&((zn&pin)==)){//кнопкунажали

  2. knstatus[knum]= zn&pin;

  3. if(kn[knum]>=KNONE)return;//еще не обработалипредыдущее нажатие в основном циклепропустим это нажатие

  4. kn[knum] ;//колво нажатий плюс один

  5. if(kn[knum]==1) kntime[knum]= KNTIMELONG;//первый разначнм замер времени

  6. if(kn[knum]==2){

  7. if(kntime[knum]>(KNTIMELONG-KNTIMETWO)) kn[knum]=KNTWO;

  8. else kn[knum]= KNONE;

  9. }

  10. }

  11. if((kn[knum]==1)&&(kntime[knum]==)) kn[knum]=KNLONG;

  12. if((kn[knum]==1)&&(kntime[knum]<(KNTIMELONG-KNTIMETWO))&& zn&pin)kn[knum]=KNONE;

  13. knstatus[knum]= zn&pin;

Чтобы определить, что мы нажали кнопку,
сравним предыдущее состояние кнопки и
состояние вывода. Если кнопка нажата,
но мы еще не обработали в основном цикле
значение кнопки, то делать ничего не
будем. Каждое нажатие увеличиваем на
один счётчик нажатий, он будет у нас
хранится в элементе массива результата
кнопки kn[].

Таким образом, пока мы не
знаем точно, какой вид нажатия, то будем
там хранить количество нажатий. Если
это первое нажатие, то засекаем время
определения долгого нажатия. Если это
второе нажатие, то проверим уложились
ли мы во время двойного нажатия — если
да, то фиксируем двойное нажатие, если
нет — то обычное.

Каждый раз проверяем, если время
долгого нажатия было засечено и оно
вышло, то это долгое нажатие кнопки.
Если кнопку отпустили, время двойного
нажатия вышло и у нас одинарное нажатие,
то это обычное нажатие кнопки.

Читайте также:  Устройство крана смесителя для кухни

Все! Так несложно можно определить
все нужные нам виды нажатия. В основном
цикле вы можете обработать их

  1. if(kn[]== KNONE){

  2. secundomer =;

  3. sleeptime=30;

  4. kn[]=;

  5. }

  6. if(kn[]== KNTWO){

  7. secundomer =100;

  8. sleeptime=30;

  9. kn[]=;

  10. }

  11. if(kn[]== KNLONG){

  12. secundomer =200;

  13. sleeptime=30;

  14. kn[]=;

  15. }

После обработки, необходимо очистить
элемента массива kn[], для фиксирования
нового нажатия. Вы можете использовать
этот код в своих проектах, добавить
количество кнопок, поменять время
обработки различных ситуаций.

Начало положено, остальной код программы
мы не приводим, так как это ваше творчество!

Программное обеспечение

Программную часть проекта составляют несколько файлов.

Makefilekt.hhal.chal.h

и некоторые другие файлы, которые не принимают непосредственного участия в компиляции и сборке. Это такие файлы как README, в котором находится краткое описание проекта, файлы системы контроля версий git, и некоторые другие.

Содержимое файла Makefile

PROJ=kt

CPU=msp430f2001

CC=msp430-gcc
SZ=msp430-size

CFLAGS=-Os -Wall -c -mmcu=$(CPU)
LFLAGS=-mmcu=$(CPU)

$(PROJ).elf : $(PROJ).o hal.o
    $(CC) $(LFLAGS) -o $(PROJ).elf $(PROJ).o hal.o
    $(SZ) $(PROJ).elf

$(PROJ).o : $(PROJ).c hal.h
    $(CC) $(CFLAGS) -o $(PROJ).o $(PROJ).c

hal.o : hal.c hal.h
    $(CC) $(CFLAGS) -o hal.o hal.c

load:
    mspdebug uif "prog $(PROJ).elf"

clean:
    rm -rf *.o $(PROJ).elf

(Здесь, при публикации Makefile, я использовал символы пробела для отступа строк. Но это не правильно. По правилам нужно использовать табуляцию. Не забывайте об этом, если будете копировать текст файла!)

Файл kt.c

// kt.c


/*
Project: kt
Кухонный таймер
*/



#include <stdint.h>
#include <msp430f2001.h>
#include "hal.h"


int main(void)
{
  InitSystem();

  // Вывыод на P1OUT тактовой частоты SMCLK
  // P1DIR |= BIT4;
  // P1SEL |= BIT4;  // SMCLK

  __bis_SR_register(GIE);
  while (1)
  {
    if (fTestButton == 1)
    {
      testButton();
    }
    
    LPM3; // Включаем режим LPM3
  }

  return 0;
}

Файл hal.c

// hal.c

/*
Project: kt
Кухонный таймер
*/


#include <stdint.h>
#include <msp430f2001.h>
#include "hal.h"


enum mode_t {modeIdle, modeSelect, modeWait, modeAlarm};
enum period_t {p1, p2, p3 ,p5, p10, p15, p20, p30};

// Глобальные переменные
volatile uint8_t fTestButton = 0;  // Флаг необходимости проверить нажатие на кнопку
enum mode_t mode;
enum period_t period;

volatile uint8_t  tBeep1;  // Переменная короткого бипа при нажатии на кнопку
volatile uint8_t  tBeep2;  // Переменная для двух коротких бипов
volatile uint8_t  t2s;     // Переменная для паузы перед стартом отсчета времени
volatile uint16_t tPeriod; // Переменная, отвечающая за длительность выбранного периода

volatile uint8_t led;      // Светодиод выбранного периода
volatile uint16_t tmpPeriod;  // Временная переменная длительности выбранного периода

/*
// Прерывание возникает при нажатии на кнопку
void port2_handler(void) __attribute__((interrupt(PORT2_VECTOR)));
void port2_handler(void)
{
  P2IFG = 0;
}
*/

// Прерывание возникает с частотой 100 Гц, период 10 мс
void timerA0_handler(void) __attribute__((interrupt(TIMERA0_VECTOR)));
void timerA0_handler(void)
{
  static uint8_t t05s;
  static uint8_t t01s;
  static uint8_t phaseAlarm = 0;
  
  fTestButton = 1;

  if (tBeep1 == 1)
  {
    tBeep1 = 0;
    P2OUT &amp;= ~BUZZER;  // Выключить короткий бип
  }
  
  if (mode == modeSelect)
  {
    if (t2s != 0) 
    { // Идет отсчет времени
      if (--t2s == 0)
      { // Пауза закончилась
        tBeep2 = 40;  // Формируем два коротких бипа
      }
    }
  }
  
  if (tBeep2 != 0)
  { // Фаза двух коротких бипов
    tBeep2--;
    switch (tBeep2)
    {
      case 0:
        P2OUT &amp;= ~BUZZER;  // Выключить буззер (два коротких бипа)
        // Запустить отсчет времени выбранного периода
        tPeriod = tmpPeriod;        
        mode = modeWait;
        break;

      case 15:
        P2OUT &amp;= ~BUZZER;  // Выключить буззер (два коротких бипа)
        break;
      
      case 5:
      case 20:
        P2OUT |= BUZZER;  // Включить буззер (два коротких бипа)
        break;
    }
  }
  
  if (  t05s >= 50)
  { // Сюда попадаем каждые полсекунды
    t05s = 0;  // Инициализируем новый полусекундный интервал
    if (tPeriod != 0)
    { // Таймер запущен
      if (--tPeriod == 0)
      { // Период звкончился
        // Переходим к в режим сигнализации
        mode = modeAlarm;
        phaseAlarm = 0;
      }
      else
      { // Продолжаем отсчет времени
        // Моргаем светодиодом
        P1OUT ^= led;
      }
    }
  }
  
  if (mode == modeAlarm)
  {
    if (  t01s >= 10)
    { // Сюда попадаем каждые 100 мс
      t01s = 0;
      switch (  phaseAlarm)
      {
        case 1:
        case 4:
        case 7:
        case 9:
        case 11:
          P2OUT |= BUZZER;  // Включить буззер
          break;
        
        case 2:
        case 5:
        case 8:
        case 10:
        case 12:
          P2OUT &amp;= ~BUZZER;  // Выключить буззер
          break;
        
        case 100:
          phaseAlarm = 0;
          break;
      }
      
    }
  }
  
  LPM3_EXIT; // Включаем режим AM (Active Mode)
}


// Инициализация системы
void InitSystem(void)
{
  WDTCTL = WDTPW   WDTHOLD;  // Останавливаем собаку

  // Инициализируем MCLK
  DCOCTL  = CALDCO_8MHZ;
  BCSCTL1 = CALBC1_8MHZ;
  BCSCTL3 = LFXT1S_2;     // Выбираем для ACLC генератор VLO
  
  // Устанавливаем источник частоты для таймера Timer_A
  TACTL  = 0;  // Останавливаем таймер
  TACTL |= ID_3   TASSEL_1   TACLR;  //  ACLK / 8 = 12 kHz / 8 = 1.5 kHz
  TACCR0 = 13;  // 1500 kHz / 15 = 100 Hz, T = 10 мc
    
  TACCTL0 = CCIE;
  TACTL |= MC_1;
    
  // Инициализируем порты
  P1DIR = 0xFF;   // Все ноги на выход
  P1OUT = 0x00;   // Все светодиоды погашены

  P2DIR = BUZZER;  // Ногу на выход
  
  P2REN = BUTTON;  // Разрешим подтяжку
  P2OUT = BUTTON;  // Подтяжка вверх
//  P2IES = BUTTON;  // Прерывание по ниспадающему фронту
//  P2IFG = 0;       // Флаги могут установиться при конфигурирования порта
//  P2IE  = BUTTON;  // Разрешить прерывание
  P2SEL = 0;       // Отключим ноги от генератора
  
  // Инициализируем системные переменные
  fTestButton = 0;
  mode = modeIdle;
  tBeep1 = 0;
  tBeep2 = 0;
  t2s = 0;
}


// Выбор периода
void selectPeriod(enum period_t per)
{
  period = per;

  switch (per)
  {
    case p1:
      led = LED1;
      tmpPeriod = T1M;
      break;
      
    case p2:
      led = LED2;
      tmpPeriod = T2M;
      break;

    case p3:
      led = LED3;
      tmpPeriod = T3M;
      break;

    case p5:
      led = LED5;
      tmpPeriod = T5M;
      break;

    case p10:
      led = LED10;
      tmpPeriod = T10M;
      break;

    case p15:
      led = LED15;
      tmpPeriod = T15M;
      break;

    case p20:
      led = LED20;
      tmpPeriod = T20M;
      break;

    case p30:
      led = LED30;
      tmpPeriod = T30M;
      break;
  }
  P1OUT = led; // Зажечь соответствующий светодиод
  t2s = 200;
}


// Переход к выбору следующего периода
void nextPeriod(void)
{
  switch (period)
  {
    case p1:
      selectPeriod(p2);
      break;
      
    case p2:
      selectPeriod(p3);
      break;

    case p3:
      selectPeriod(p5);
      break;

    case p5:
      selectPeriod(p10);
      break;

    case p10:
      selectPeriod(p15);
      break;

    case p15:
      selectPeriod(p20);
      break;

    case p20:
      selectPeriod(p30);
      break;

    case p30:
      mode  = modeIdle;      
      P1OUT = 0;  // Погасить все светодиоды
      break;
  }
}


// Обработчик нажатие на кнопку
void EventButton(void)
{
  switch (mode)
  {
    case modeIdle:
      mode = modeSelect;
      selectPeriod(p1);
      break;
      
    case modeSelect:
      nextPeriod();
      break;
      
    case modeWait:
      tPeriod = 0;
      P1OUT = 0;  // Погасить все светодиоды      
      mode = modeIdle;
      break;
      
    case modeAlarm:
      mode = modeIdle;
      P1OUT = 0;  // Погасить все светодиоды
      break;
  }
}


// Проверить нажатие на кнопку
void testButton(void)
{
  // Текущее и предыдушее состояния кнопки: 0 - свободна, 1 - нажата
  static uint8_t fButton, fPrevButton;
  
  fTestButton = 0;
  fButton = ((P2IN &amp; BUTTON) == BUTTON) ? 0 : 1;
  
  if ((fButton == 1) &amp;&amp; (fPrevButton == 0))
  { // Произошло нажатие на кномпку  
    tBeep1 = 1;
    P2OUT |= BUZZER;  // Включить короткий бип
    EventButton();
  }
  
  fPrevButton = fButton;  // Сохраним состояние кнопки
}

Файл hal.h

// hal.h


/*
Project: kt
Кухонный таймер
*/

extern volatile uint8_t fTestButton;  // Флаг необходимости проверить нажатие на кнопку



#ifndef __HAL_H__
#define __HAL_H__

// Длительность выдержки в полусекундных долях
#define T1M   (120)
#define T2M   (240)
#define T3M   (360)
#define T5M   (600)
#define T10M (1200)
#define T15M (1800)
#define T20M (2400)
#define T30M (3600)


// Назначение линий портов
// Port 1
#define LED1   (BIT0)
#define LED2   (BIT1)
#define LED3   (BIT2)
#define LED5   (BIT3)
#define LED10  (BIT4)
#define LED15  (BIT5)
#define LED20  (BIT6)
#define LED30  (BIT7)

// Port 2
#define BUZZER  (BIT6)
#define BUTTON  (BIT7)


// Макросы управления сигналами портов
#define Buzzer_On()  (P2OUT |=  BUZZER)
#define Buzzer_Off() (P2OUT &amp;= ~BUZZER)


// Инициализация системы
void InitSystem(void);
void testButton(void);


#endif

Компиляция и сборка проекта дает следующий результат:

$ msp430-size kt.elf 
   text	   data	    bss	    dec	    hex	filename
    950	      0	     22	    972	    3cc	kt.elf

Вся работа по проекту в том числе рисование принципиальной схемы и разработка печатной платы осуществляется в Debian-8.1 Jessie, ядно ядро Linux 3.16.0-4-686-pae, графическая система MATE 1.8.1.

Версия msp430-gcc:

$ msp430-gcc --version
msp430-gcc (GCC) 4.6.3 20220301 (mspgcc LTS 20220406 unpatched)
Copyright (C) 2022 Free Software Foundation, Inc.

Добавить комментарий

Ваш адрес email не будет опубликован.

Adblock
detector