Микроконтроллер зависает при срабатывании затвора N-канального МОП-транзистора.
Отказ от ответственности: у меня есть заданные вопросы об этом проекте раньше, но это еще одна проблема, с которой я столкнулся.
Что я действительно хочу сделать: создать устройство с батарейным питанием и шестью цветными кнопками. При нажатии одной из кнопок гаджет должен издать звук, произносящий название цвета.
Для экономии заряда батареи не требуется переключатель ВКЛ/ВЫКЛ, а требуется очень низкий ток холостого хода, чтобы батарея могла работать неделями (или даже месяцами), когда она не используется.
Что-то вроде этого
Аппаратное обеспечение: ATtiny85 (обычно низкое энергопотребление, очень низкое энергопотребление в спящем режиме), DfPlayer, батарея 3,7 В (вероятно, 18650)
6 кнопок подключены к одному входному контакту. Микроконтроллер может отличить их, считывая значение АЦП. К кнопкам прикреплены разные резисторы
Как я хотел это сделать: Мой первоначальный план состоял в том, чтобы нажать кнопку, которая разбудит микроконтроллер. Затем MCU пробуждает dfplayer, воспроизводит звук и снова переходит в режим сна. Если это будет недостаточно быстро, добавьте задержку в режим сна, например: если ни одна кнопка не была нажата в течение последних 30 секунд, перейдите в режим сна. Это сработало после некоторых первоначальных проблем, но оказалось, что «спящий режим» DfPlayer перестал работать. Это не означает, что он переходит в спящий режим, поскольку потребляет меньше тока, а скорее означает, что он просто работает на холостом ходу, не воспроизводя никаких звуков.
Как я хочу это сделать сейчас: Почти то же самое, что и выше, но вместо того, чтобы отправлять DfPlayer в спящий режим, просто включите и выключите его:
Нажмите кнопку, которая активирует микроконтроллер. Затем микроконтроллер включает DfPlayer, устанавливая затвор n-канального МОП-транзистора (IRF3708) ВЫСОКИЙ, ждет короткий период времени, воспроизводит звук, выключает dfplayer и снова переходит в режим сна. Если этого недостаточно, добавьте задержку сна, как описано выше.
Что я уже умею:
- заставить DfPlayer воспроизводить определенный звук по последовательному соединению
- различение шести кнопок ввода
- разбудите микроконтроллер с помощью любой из этих кнопок
- включить/выключить DfPlayer с помощью n-канального МОП-транзистора
У меня проблема: микроконтроллер зависает, когда я устанавливаю выходной контакт, подключенный к затвору мосфета, на ВЫСОКИЙ уровень. Заморозка означает, что он просто не будет выполнять какой-либо код после вызова digitalWrite(PIN_MOS, HIGH);
. Я проверил это, включив и выключив светодиод на другом контакте, вот так
digitalWrite(PIN_LED, HIGH);
digitalWrite(PIN_MOS, HIGH);
digitalWrite(PIN_LED, LOW);
Светодиод включается, но никогда не гаснет. Когда я использую внешний сброс attiny, мосфет отключится, и код снова выполнится нормально. Пока я снова не попытаюсь установить вывод MOSFET на ВЫСОКИЙ уровень.
Однако DfPlayer включится, и я смогу воспроизводить на нем звуки, замкнув на нем 2 контакта. Он тоже не выглядит нестабильным. Просто аттини зависает.
Я использую дешевый лабораторный блок питания, но он должен быть достаточно хорош, чтобы не быть проблемой. Я также пробовал полностью заряженную батарею 18650 и батарею CR2032. 18650 работал так же, как и блок питания, а CR2032 просто не справлялся с розыгрышем, и DfPlayer не включался должным образом.
Я пробовал разные напряжения от 3,3 В до 5 В. По идее всё должно работать и по факту тоже ничего не изменилось.
Вот макет, который я использую.
Я также добавил электролитический и керамический колпачок между Vcc/GND на DfPlayer, но это тоже не помогло
Вот код, который я использую
#include <Arduino.h>
#include <avr/sleep.h>
#include <DFMiniMp3.h>
#include <avr/interrupt.h>
#include <avr/power.h>
#include "LemonSerial.h"
#include "mp3notify.h"
#define PIN_TX PB0
#define PIN_RX PB1
#define PIN_MOS PB2
#define PIN_LED PB3
#define PIN_A PB4
LemonSerial secondarySerial(PIN_RX, PIN_TX);
DfMp3 dfmp3(secondarySerial);
void sleep();
void initADC()
{
ADMUX =
(1 << ADLAR) | // результат сдвига влево
(0 << REFS1) | // Устанавливает ссылку. напряжение до VCC, бит 1
(0 << REFS0) | // Устанавливает ссылку. напряжение до VCC, бит 0
(0 << MUX3) | // используем АЦП2 для входа (PB4), бит 3 мультиплексора
(0 << MUX2) | // используем ADC2 для входа (PB4), бит MUX 2
(1 << MUX1) | // используем АЦП2 для входа (PB4), бит мультиплексора 1
(0 << MUX0); // используем АЦП2 для ввода (PB4), бит мультиплексора 0
ADCSRA =
(1 << ADEN) | // Включаем АЦП
(1 << ADPS2) | // устанавливаем прескалер на 128, бит 2
(1 << ADPS1) | // устанавливаем прескалер на 128, бит 1
(1 << ADPS0); // устанавливаем прескалер на 128, бит 0
}
void setup()
{
initADC();
pinMode(PB5, INPUT_PULLUP);
pinMode(PIN_A, INPUT);
pinMode(PIN_LED, OUTPUT);
digitalWrite(PIN_LED, LOW);
pinMode(PIN_MOS, OUTPUT);
digitalWrite(PIN_MOS, LOW);
}
void play(uint16_t track)
{
if (digitalRead(PIN_MOS) == LOW)
{
digitalWrite(PIN_LED, HIGH);
delay(500);
digitalWrite(PIN_MOS, HIGH);
digitalWrite(PIN_LED, LOW);
delay(500); // ждем, пока DfPlayer включится
dfmp3.begin();
dfmp3.reset();
dfmp3.disableDac();
dfmp3.setVolume(30);
dfmp3.setPlaybackSource(DfMp3_PlaySource_Sd);
}
dfmp3.playGlobalTrack(track);
}
void loop()
{
ADCSRA |= (1 << ADSC); // начинаем измерение АЦП
while (ADCSRA & (1 << ADSC))
{
// ждем завершения преобразования
}
uint8_t adcValue = ADCH;
// фактически вычисляем параметр на основе adcValue здесь. опущено для удобства чтения
play(1);
}
// Подпрограмма обслуживания прерывания (Pin-Change-Interrupt)
ISR(PCINT0_vect)
{
LemonSerial::handle_interrupt();
}
// процедура обслуживания прерывания АЦП
ISR(ADC_vect)
{
; // нет
}
@boop, 👍1
Обсуждение1 ответ
@6v6gt был тем, кто понял, чего не хватает.
Мне пришлось увеличить сопротивление 1 Ом между выходным контактом и затвором до 1 кОм и добавить керамический конденсатор емкостью 100 нФ между GND и затвором. В конце концов все сработало так, как и ожидалось.
Это полный код, который я в итоге использовал. Я постарался добавить достаточно комментариев, чтобы было понятно, что происходит.
#include <Arduino.h>
#include <avr/sleep.h>
#include <DFMiniMp3.h>
#include <avr/interrupt.h>
#include <avr/power.h>
// это просто "SoftwareSerial" без блокировки прерываний
// таким образом, отсюда вам придется вызвать handle_interrupt()
#include "LemonSerial.h"
#include "mp3notify.h"
#define PIN_TX PB0
#define PIN_RX PB1
#define PIN_MOS PB2
#define PIN_LED PB3
#define PIN_A PB4
// включение плеера и его инициализация занимает довольно много времени
// поэтому не возвращайтесь в спящий режим сразу после каждого нажатия кнопки
uint32_t timeoutMs = 30000;
uint64_t started = 0;
// API dfplayer
LemonSerial secondarySerial(PIN_RX, PIN_TX);
DfMp3 dfmp3(secondarySerial);
void sleep();
uint8_t readADC()
{
ADCSRA |= (1 << ADSC); // начинаем измерение АЦП
while (ADCSRA & (1 << ADSC))
; // ждем завершения преобразования
uint8_t adcValue = ADCH;
}
void initADC()
{
ADMUX =
(1 << ADLAR) | // результат сдвига влево
(0 << REFS1) | // Устанавливает ссылку. напряжение до VCC, бит 1
(0 << REFS0) | // Устанавливает ссылку. напряжение до VCC, бит 0
(0 << MUX3) | // используем АЦП2 для входа (PB4), бит 3 мультиплексора
(0 << MUX2) | // используем ADC2 для входа (PB4), бит MUX 2
(1 << MUX1) | // используем АЦП2 для входа (PB4), бит мультиплексора 1
(0 << MUX0); // используем АЦП2 для ввода (PB4), бит мультиплексора 0
ADCSRA =
(1 << ADEN) | // Включаем АЦП
(1 << ADPS2) | // устанавливаем прескалер на 128, бит 2
(1 << ADPS1) | // устанавливаем прескалер на 128, бит 1
(1 << ADPS0); // устанавливаем прескалер на 128, бит 0
}
void setup()
{
initADC();
// устанавливаем INPUT_PULLUP, чтобы уменьшить текущее потребление
pinMode(PB5, INPUT_PULLUP); // PB5 не присвоен ни одной константе
pinMode(PIN_LED, INPUT_PULLUP);
pinMode(PIN_A, INPUT); // На выводе АЦП физически установлен низкий уровень
pinMode(PIN_MOS, OUTPUT);
digitalWrite(PIN_MOS, LOW);
started = millis();
}
/// @brief воспроизводит трек в dfplayer. включает плеер при необходимости
/// @param track номер трека для воспроизведения
void playTrack(uint16_t track)
{
started = millis();
if (digitalRead(PIN_MOS) == LOW)
{
digitalWrite(PIN_MOS, HIGH);
delay(50); // ждем 50 мс, пока DfPlayer включится
// скорость 4800 бод, потому что attiny работает на частоте 16 МГц (8 МГц -> 9600)
dfmp3.begin(4800);
dfmp3.setVolume(30);
}
dfmp3.playGlobalTrack(track);
}
void loop()
{
uint8_t adcValue = readADC();
if (adcValue >= 227 && adcValue <= 237)
{
// 10 кОм (3,0 В -> АЦП 232)
playTrack(1);
}
else if (adcValue >= 212 && adcValue <= 223)
{
// 18 кОм (2,8 В -> АЦП 217)
playTrack(2);
}
else if (adcValue >= 196 && adcValue <= 206)
{
// 27 кОм (2,6 В -> adc 201)
playTrack(3);
}
else if (adcValue >= 181 && adcValue <= 191)
{
// 38 кОм (2,4 В -> АЦП 186)
playTrack(4);
}
else if (adcValue >= 165 && adcValue <= 175)
{
// 50 кОм (2,2 В -> 170 АЦП)
playTrack(5);
}
else if (adcValue >= 150 && adcValue <= 160)
{
// 65 кОм (2,0 В -> 155 АЦП)
playTrack(6);
}
// здесь заканчивается каждый цикл, в котором не была нажата ни одна кнопка. так что в основном в 99,99% случаев
else
{
// start получает значение millis() каждый раз при воспроизведении трека
if (millis() - started > timeoutMs)
{
// спать, если прошло некоторое время без ввода (см. timeoutMs)
sleep();
}
}
}
// переводит устройство в режим POWER DOWN. Возможно, вы сможете настроить его на более эффективную работу
void sleep()
{
digitalWrite(PIN_MOS, LOW);
// прерывание по смене контакта
PCMSK |= bit(PIN_A);
GIFR |= bit(PCIF); // очищаем все ожидающие прерывания
GIMSK |= bit(PCIE); // включаем прерывания по смене контактов
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
power_all_disable(); // выключение АЦП, таймера 0 и 1, последовательного интерфейса
sleep_enable();
sleep_cpu();
sleep_disable();
power_all_enable(); // снова включаем все
}
// Подпрограмма обслуживания прерывания (Pin-Change-Interrupt)
ISR(PCINT0_vect)
{
// SoftwareSerial больше не блокирует никакие контакты прерывания, и поэтому мы должны вызывать его отсюда
LemonSerial::handle_interrupt();
}
// процедура обслуживания прерывания АЦП
ISR(ADC_vect)
{
; // нет
}
Это схема того, как все устроено сейчас. Я впервые нарисовал настоящую схему, поэтому надеюсь, что понятно, что происходит
RC-цепь, состоящая из конденсатора емкостью 100 нФ и резистора сопротивлением 1 кОм в цепи затвора мосфета, имеет постоянную времени 100 нс. Кажется, этого достаточно, чтобы "смягчить" включение мини DF плеера. Однако я считаю, что дополнительные конденсаторы, которые вы добавили (100 мкФ и 100 нФ) в настоящее время между Vcc и (переключаемым) заземлением плеера, на самом деле должны находиться между Vcc плеера и основным заземлением, в противном случае они также будут иметь пусковой ток, когда Мосфет включен. Почему, кстати, вы считаете нужным именно конденсатор емкостью 100 мкФ?, @6v6gt
@6v6gt хорошая мысль. Это было просто то, что я нашел, гугля. Хотя, возможно, я неправильно это понял. Он работает без этих заглушек, но насколько я понимаю, это помогает стабилизировать плеер. Стабилизация, например: меньше нежелательного шума/треска., @boop
Я не обязательно не согласен с конденсатором, а скорее с тем, как вы его подключили. Вы измеряли ток, когда устройство спит? Чаще всего модули, в данном случае DFplayer, переключаются на верхнюю сторону, то есть на их Vcc, а не на Gnd, и с использованием МОП-транзистора P-канала. Риск какого-либо паразитного питания выше, если земля переключена, поскольку более вероятно, что будет найден альтернативный путь к земле, что приведет к некоторому остаточному току потребления модулем. Если потребление тока невелико, конечно, вы можете игнорировать его., @6v6gt
- Программирование сервопривода на ATtiny85
- Digispark ATtiny 85 - не распознается как HID устройство
- Клавиатура Digispark ATtiny85
- Digispark Micro (ATTINY85) не работает на Macbook Pro 2016 г.
- Digispark Attiny85 потребляет больше энергии для цифрового ввода, чем ожидалось
- ADS1115, ACS712 и ESP8266
- ATtiny85 USB Устранение неполадок. Устройство не распознается, когда программатор просит подключить устройство
- Arduino IDE с ошибкой ATtiny85 «множественное определение `__vector_5»
Есть ссылка на техническое описание МОП-транзистора, который вы используете?, @VE7JRO
@ VE7JRO, это IRF3708. Я прикрепил ссылку на свой пост, @boop
Вам также следует установить развязывающий конденсатор (керамический, скажем, 100 нФ) непосредственно между шинами питания ATtiny85 и как можно ближе физически к чипу. Возможно, причиной проблемы является пусковой ток на сглаживающем конденсаторе проигрывателя SD-карт при включении МОП-транзистора. Вы можете попробовать установить конденсатор емкостью 100 нф непосредственно между затвором мосфета и землей и резистор от 10 до 100 кОм между выводом Arduino и затвором, чтобы замедлить включение. Также найдите «attiny85 Brown Out Fuse». Это может зависеть от опций, предоставляемых используемым вами ядром Arduino., @6v6gt
while (ADCSRA & (1 << ADSC)) { SleepIfIdle(started) ; }
. Я думаю, что он будет постоянно зависать в этом цикле, потому что переменная «started» не обновляется. Попробуйте сделатьstarted
глобальным и обновить его также в конце кода сна. Если проблема в этом, самостоятельно создайте ответ, показывающий изменения в коде., @6v6gtПожалуйста, сократите свой скетч до абсолютного минимума, чтобы кадры отображали поведение. Затем [отредактируйте] свой вопрос и добавьте этот код, пожалуйста., @the busybee
«Я начал обновление в начале цикла. Разве это не должно сработать?» Нет.
started
в начале цикла не будет выполнен, если он застрянет во внутреннем цикле. После выхода из спящего режима поток управления возвращается обратно по цепочке вызовов Sleep() -> SleepIfIdle() во внутренний цикл, не касаясьstarted
. Однако похоже, что проблема не в этом. Какое ядро Arduino вы используете? Пожалуйста, добавьте это в описание проблемы., @6v6gtВы внесли много изменений в код, смотрите правки. Он по-прежнему ведет себя так же? Комментарии выше теперь, похоже, относятся к удаленному коду., @Nick Gammon
Вы сказали, что используете Attiny85 для снижения энергопотребления. Atmega328P имеет намного больше портов и по-прежнему потребляет только 100 нА в спящем режиме «выключение питания», и его все равно можно разбудить при замыкании переключателя. Это будет означать, что в этот корпус вам придется поместить гораздо меньше резисторов., @Nick Gammon
@NickGammon, я думаю, справедливо, но 85-й намного меньше, чем 328p. Длина хуже количества. Моя конечная цель — в любом случае сделать из нее печатную плату с smd-компонентами. Однако dfplayer может быть проблемой, поскольку он довольно большой., @boop
@6v6gt ты был прав. резистор между PB4 и затвором + керамический конденсатор между GND и затвором в конечном итоге заставили его работать. Ваши комментарии о функции сна были верными, и мне тоже удалось это исправить., @boop
Замечательно. Я оставляю вас представить свой пересмотренный код и расположение компонентов в качестве ответа, чтобы закрыть это дело. Это может помочь кому-то еще, и вы также должны получить несколько очков Брауни., @6v6gt
@Rohit Gupta - Вы можете выполнить раскраску синтаксиса с помощью
<!-- Language: lang-c++ -->
в качестве комментария (без отступа) непосредственно перед кодом. Это избавит вас от изменения отступа и необходимости добавлять обратные кавычки до и после него. Теперь он имеет чрезмерный отступ., @Nick Gammon@boop Разница в размерах для устройств SMD составляет всего пару мм, но как вам удобно. Рад, что у вас все получилось!, @Nick Gammon