(Arduino Uno) ШИМ-выход (и подключенный к нему сервопривод) становится нестабильным, если я использую 1 провод на другом выводе
Ненужный контекст но эй я предполагаю что вам может быть интересно
Привет, я хочу построить ПИД-регулятор температуры с сервоприводом, который нажимает на кремниевую трубу, чтобы регулировать поток жидкости, проходящей через нее. Жидкость представляет собой горячее пиво, которое должно быть очень быстро охлаждено от 100 °C до 25 °C, и оно проходит через теплообменник, где также проходит в другом направлении постоянный поток холодной воды. Чем меньше поток пива, тем холоднее оно становится. Но регулировать его вручную громоздко, и я хотел бы автоматизировать его, потому что я (думаю, что могу).
Обзор системы
Моя система в основном состоит из Arduino Uno, серводвигателя (ШИМ на выводе 11), датчика температуры BS18B20 1Wire (на выводе 2) и нескольких кнопок. В "ручном" режиме я могу выбрать положение сервопривода с помощью кнопок, и он очень стабилен.
У меня также есть зуммер на выводе 13, и я добавлю экран I2C, когда ядро будет работать.
Моя проблема
Как только я начинаю запрашивать температуру через регулярные промежутки времени (например, каждые 200 мс), сервопривод все время мерцает, что скучно, выглядит плохо и, вероятно, сократит его продолжительность жизни. Я думаю, что каждый раз, когда я запрашиваю температуру, что-то происходит с ШИМ, например, короткий сброс или что-то в этом роде, и сервопривод реагирует, а затем возвращается в исходное положение.
Единственное питание, которое я тестировал до сих пор, - это USB-кабель от компьютера. На случай, если вам интересно, код здесь:
#include <AceButton.h> // https://github.com/bxparks/AceButton
#include <OneWire.h>
#include <DallasTemperature.h>
#include <Servo.h>
Servo myservo; // создать объект servo для управления сервоприводом
// двенадцать сервообъектов могут быть созданы на большинстве плат
#define ONE_WIRE_BUS 2 // контакт для термометра
using namespace ace_button;
// контакт, прикрепленный к кнопке.
const int BUTTON_SET_PIN = 12;
const int BUTTON_MORE_PIN = 4;
const int BUTTON_LESS_PIN = 0;
const int BUTTON_MODE2_PIN = 5;
const int BUTTON_MODE3_PIN = 7;
const int LED_PIN = LED_BUILTIN;
const int SERVO_PIN = 11;
const int MODE_THERMOMETER = 1;
const int MODE_REGULATOR = 2;
const int MODE_MANUAL = 3;
int mode;
// Светодиодные состояния. Некоторые микроконтроллеры подключают свой встроенный светодиод наоборот.
const int LED_ON = HIGH;
const int LED_OFF = LOW;
// Обе кнопки автоматически используют системный ButtonConfig по умолчанию. Альтернативой
// является вызов метода AceButton::init() в setup() ниже.
AceButton buttonSet(BUTTON_SET_PIN);
AceButton buttonMore(BUTTON_MORE_PIN);
AceButton buttonLess(BUTTON_LESS_PIN);
ButtonConfig modeConfig;
AceButton buttonMode2(&modeConfig);
AceButton buttonMode3(&modeConfig);
// Настройка экземпляра OneWire для связи с любыми устройствами OneWire
// (не только Maxim / Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);
// Передайте нашу ссылку OneWire на температуру Далласа.
DallasTemperature sensors(&oneWire);
// переменные для настройки
float value = 50;
unsigned long tuneStart;
float max = 175;
float min = 10;
void handleModeEvent(AceButton *, uint8_t, uint8_t);
void handleModeEvent(AceButton *button, uint8_t eventType, uint8_t /* buttonState */) {
switch (eventType) {
case AceButton::kEventPressed:
if (button->getPin() == BUTTON_MODE2_PIN) {
mode = MODE_REGULATOR;
printMode();
}
if (button->getPin() == BUTTON_MODE3_PIN) {
mode = MODE_MANUAL;
printMode();
}
break;
case AceButton::kEventReleased:
if (button->getPin() == BUTTON_MODE2_PIN) {
mode = MODE_THERMOMETER;
printMode();
}
if (button->getPin() == BUTTON_MODE3_PIN) {
mode = MODE_REGULATOR;
printMode();
}
break;
}
}
void printMode()
{
Serial.print("Mode ");
Serial.println(mode);
}
void handleButtonEvent(AceButton *, uint8_t, uint8_t);
void setup() {
Serial.begin(9600);
while (!Serial); // Подождите, пока Serial не будет готов - Leonardo/ Micro
Serial.println(F("Test 3 boutons - la suite en 115200 bps"));
delay(300);
// servo
myservo.attach(11);
// thermal probe
sensors.begin();
sensors.setWaitForConversion(false);
sensors.setResolution(10);
Serial.begin(115200);
while (!Serial); // Подождите, пока Serial не будет готов - Leonardo/ Micro
Serial.println(F("setup(): begin"));
// Инициализировать встроенный светодиод в качестве выхода.
pinMode(LED_PIN, OUTPUT);
pinMode(SERVO_PIN, OUTPUT);
// Buttons use the built-in pull up register.
pinMode(BUTTON_SET_PIN, INPUT_PULLUP);
pinMode(BUTTON_MORE_PIN, INPUT_PULLUP);
pinMode(BUTTON_LESS_PIN, INPUT_PULLUP);
pinMode(BUTTON_MODE2_PIN, INPUT_PULLUP);
pinMode(BUTTON_MODE3_PIN, INPUT_PULLUP);
buttonMode2.init(BUTTON_MODE2_PIN);
buttonMode3.init(BUTTON_MODE3_PIN);
// Настройте ButtonConfig с помощью обработчика событий и включите все более высокие
// события уровня.
ButtonConfig *buttonConfig = ButtonConfig::getSystemButtonConfig();
buttonConfig->setEventHandler(handleButtonEvent);
buttonConfig->setFeature(ButtonConfig::kFeatureClick);
buttonConfig->setFeature(ButtonConfig::kFeatureLongPress);
buttonConfig->setFeature(ButtonConfig::kFeatureRepeatPress);
// Configs для кнопок tune-up и tune-down. Нужно RepeatPress вместо
// ЛонгПресс.
modeConfig.setEventHandler(handleModeEvent);
modeConfig.setFeature(ButtonConfig::kFeatureClick);
// Эти подавления на самом деле не нужны, но чище.
modeConfig.setFeature(ButtonConfig::kFeatureSuppressAfterClick);
// Проверьте, была ли нажата кнопка при загрузке
if (buttonMode3.isPressedRaw()) {
mode = MODE_MANUAL;
printMode();
} else if (buttonMode2.isPressedRaw()) {
mode = MODE_THERMOMETER;
printMode();
} else {
mode = MODE_REGULATOR;
printMode();
}
Serial.println(F("setup(): ready"));
}
long timer_200 = 0;
long timer_1000 = 0;
void loop() {
// Должен вызываться каждые 4-5 мс или быстрее, для времени деблокирования по умолчанию ~ 20 мс.
buttonSet.check();
buttonMore.check();
buttonLess.check();
buttonMode2.check();
buttonMode3.check();
// каждые 200 мс
long now = millis();
if (now > timer_200 + 200) {
timer_200 = now; // для повторного запуска таймера
if (mode == MODE_MANUAL) {
myservo.write(value);
}
}
// каждый 1s
if (now > timer_1000 + 1000) {
timer_1000 = now; // для повторного запуска таймера
// получить температуру, запрошенную в последний раз
Serial.print("Temperature for Device is: ");
Serial.println(sensors.getTempCByIndex(0));
// и запросить новый
sensors.requestTemperatures(); // Отправить команду для получения температуры
if (mode == MODE_THERMOMETER) {}
}
}
// Обработчик событий для обеих кнопок.
void handleButtonEvent(AceButton *button, uint8_t eventType, uint8_t buttonState) {
switch (eventType) {
case AceButton::kEventClicked:
if (button->getPin() == BUTTON_MORE_PIN) {
value = value * 1.01;
if (value > max) {
value = max;
}
Serial.println(value);
}
if (button->getPin() == BUTTON_LESS_PIN) {
value = value * .99;
if (value < min) {
value = min;
}
Serial.println(value);
}
if (button->getPin() == BUTTON_SET_PIN) {
Serial.println(F("set"));
}
break;
case AceButton::kEventLongPressed:
if (button->getPin() == BUTTON_SET_PIN) {
Serial.println(F("set long press"));
}
if (button->getPin() == BUTTON_MORE_PIN || button->getPin() == BUTTON_LESS_PIN) {
tuneStart = millis();
}
break;
case AceButton::kEventRepeatPressed:
if (button->getPin() == BUTTON_MORE_PIN) {
tuneUp();
}
if (button->getPin() == BUTTON_LESS_PIN) {
tuneDown();
}
break;
}
}
void tuneUp() {
unsigned long duration = millis() - tuneStart;
float ratio = 1 + (0.00001f * 4 * duration);
value = value * ratio;
if (value > max) {
value = max;
}
Serial.println(value);
}
void tuneDown() {
unsigned long duration = millis() - tuneStart;
float ratio = 1 - (0.00001f * 4 * duration);
value = value * ratio;
if (value < min) {
value = min;
}
Serial.println(value);
}
Это все еще прототип.
Что может быть причиной этого и как я могу это предотвратить ?
1 ответ
Лучший ответ:
Я подозреваю, что 1Wire libary отключает (и включает) прерывания, когда считает нужным. Это вызовет "заминки" сервопривода, потому что библиотека сервоприводов использует прерывания.
Если я прав, то лучше всего использовать сервобиблиотеку, которая не использует прерывания. Я всегда использую аппаратную библиотеку ШИМ для управления сервоприводами, поэтому у меня нет рекомендаций по библиотеке сервоприводов, но, возможно, у кого-то еще есть.
Кроме того: вы делаете Serial.begin()
дважды, и переменные, которые вы используете для синхронизации (используя millis()
), должны быть беззнаковыми
, а не длинными
.
Спасибо, это очень интересно! Мне действительно не нужна библиотека сервоприводов, вывод обычного ШИМ, скорее всего, будет работать без проблем для моего приложения. Я попробую сделать это завтра и дам вам знать., @Guillaume Deshors
Это проблема использовать Serial.begin дважды? Я делаю это специально, потому что мне нравится знать, что в данный момент загружено на данную плату arduino, поэтому я начинаю с сообщения в 9600 бит/с с именем скетча и скоростью следующих сообщений, а затем повторно запускаю его с указанной скоростью. Кажется, это работает :), @Guillaume Deshors
Это не обязательно проблема, но зачем вам это делать, если принимающее приложение, по-видимому, может справиться с более высоким коэффициентом бодрости в любом случае? Но если вы делаете это специально, то все в порядке., @ocrdu
Идея состояла в том, чтобы всегда начинать с 9600 в качестве стандартного скетча, который я загружаю, а затем свободно выбирать скорость связи. В противном случае я могу позже не вспомнить, какую скорость я должен установить в консоли, чтобы общаться с ней. Это довольно свежая идея, которая пришла мне в голову, и я не знаю, будет ли она настолько полезной., @Guillaume Deshors
Скорее всего, вы были правы. Я сделал пользовательский код для вывода нужного мне сигнала, который * на самом деле не является ШИМ*, см. https://forum.arduino.cc/index.php?topic=530091.0 и это решило проблему. Большое спасибо !, @Guillaume Deshors
Мы вам очень рады. Однако сервосигнал * - это * ШИМ, обычно выровненный по левому краю и с фиксированной частотой, но давайте не будем сейчас это обсуждать., @ocrdu
- Управление скоростью вентилятора с помощью библиотеки Arduino PID
- Как устранить шум от вентилятора 12 В с ШИМ-управлением на низкой скорости
- Генерация частоты ШИМ выше 125 кГц с помощью Arduino Uno
- Увеличить разрядность PWM
- Как вывести истинное аналоговое напряжение на выходной контакт
- ПИД-регулятор для управления скоростью двигателя
- Как управлять 6 шаговыми двигателями с помощью Arduino?
- Синусоидальный инвертор
Что вы используете для своего контроллера? Некоторые устройства не имеют аппаратного ШИМ, и даже некоторые из них используют "битный стук", если вы не выбрали правильный вывод., @jwh20
Извините, я забыл главный компонент - arduino Uno. Я отредактировал свой вопрос., @Guillaume Deshors