如何按需删除和重启 esp32 arduino(步进电机控制器应用程序)的硬件定时器(用于中断)
How to delete and restart hw timer (for interrupts) on demand for esp32 arduino (stepper motor controller application)
我无法弄清楚如何禁用然后重新启用(在触发事件时)来自 esp-arduino 库的 hw (esp32-hal-timer) 定时器,here 用于步进电机我的 esp32 开发板的控制器应用程序。它会倒计时并根据需要多次触发 ISR,但是当我禁用它时(这样 ISR 就不会被不必要地调用),当我再次尝试启动它时它不会启动。奇怪的是它以与第一次相同的方式启动,所以我不确定这是我的代码问题还是特定库处理垃圾收集的方式。这也是我第一次尝试使用中断。我的代码如下。
为了避免费力太多,一般流程是在setup方法中初始化定时器(称为motorTimer),然后连接wifi,在mqtt的回调方法中对任何带有整数负载的消息将触发 motor.h class 中的 'moveTo' 方法,然后在触发 ISR 计时器时将触发同一个 class 中的更新方法。然后定时器将在每次迭代中更改其时间,以进行加速度补偿。这很好用,直到需要终止计时器然后稍后重新启动它 - 然后根本不会调用 ISR,就像计时器没有正确停止一样。这就是我的问题所在。
#include <SPI.h>
#include <Adafruit_MAX31855.h>
#include <WiFi.h>
#include <PubSubClient.h>
#include <Wire.h>
#include "VCDevices.h"
// Replace the next variables with your SSID/Password combination
// //WiFi info:
const char* ssid = "SSID";
const char* password = "PASSWORD_HERE";
// Add your MQTT Broker IP address, example:
const char* mqtt_server = "home.IOT.lan";
WiFiClient espClient;
PubSubClient client(espClient);
long lastMsg = 0;
char topArr[50];
char msgArr[100];
// get size of array first, to feed to for loop
int numDevices = sizeof(devices)/sizeof(device);
int value = 0;
unsigned long heartbeat_previousMillis = 0;
unsigned long motorCheck_previousMillis = 0;
const long timeOut = 60000;
const long motorCheckTime = 600;
hw_timer_t * motorTimer = NULL;
bool state = 0;
int count = 0;
int d = 0;
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
bool finished = false;
enum LogLevel
{
Debug, //Sends message only to the serial port
Error, //Sends message over MQTT to 'Errors' topic, and to serial port with "Error: " pre-appended
Message //Sends message over serial and MQTT to 'StatusMessage' topic
};
///****** TIMER LOGIC HERE ******
//TODO: timer needs to just figure out what time the next pulse needs to be fired at - needs to be calculated on the fly
//This is calculated inside the Motor class.
void IRAM_ATTR motorInterrupt(void)
{
Serial.println("B");
portENTER_CRITICAL(&timerMux);
noInterrupts();
//check if the motor is in motion still
if (!linMotor.getMotorStatus())
{
d = linMotor.Update();
timerAlarmWrite(motorTimer, d, true);
timerAlarmEnable(motorTimer);
}
else
{
// timerAlarmWrite(motorTimer, 1, true);
timerAlarmDisable(motorTimer);
finished = true;
}
//kill the timer and interrupt if not
interrupts();
portEXIT_CRITICAL(&timerMux);
}
//****** END TIMER HERE *****
void log(LogLevel level, String message)
{
switch(level)
{
case LogLevel::Debug:
Serial.println(message);
break;
case LogLevel::Error:
print(ErrorTopic, message);
break;
case LogLevel::Message:
Serial.print("Message: ");
Serial.println(message);
print(StatusTopic, message);
break;
}
}
void print(char topic[], String message)
{
Serial.print(topic);
Serial.print(" : ");
Serial.println(message);
//topic.toCharArray(topArr, sizeof(topic)+2);
message.toCharArray(msgArr, sizeof(msgArr));
client.publish(topic, msgArr, message.length());
}
// WiFi methods are located below:
void setup_wifi()
{
delay(10);
// We start by connecting to a WiFi network
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}
void callback(char* topic, byte* message, unsigned int length)
{
Serial.print("Message arrived on topic: ");
Serial.print(topic);
Serial.print(". Message: ");
String messageTemp;
String response;
//check if heartbeat signal was sent
if (String(topic) == HeartbeatTopic)
{
heartbeat_previousMillis = millis();
}
for (int i = 0; i < length; i++) {
Serial.print((char)message[i]);
messageTemp += (char)message[i];
}
Serial.println();
// Iterate through each device and update states accordingly
for (int i = 0; i < numDevices; i = i + 1)
{
// the char arrays need to be cast as strings to compare to each other
if (String(topic) == String(devices[i].controlTopic))
{
if (messageTemp == "on")
{
digitalWrite(devices[i].pinNumber, devices[i].shouldInvert?LOW:HIGH);
response = "on";
}
else
{
digitalWrite(devices[i].pinNumber, devices[i].shouldInvert?HIGH:LOW);
response = "off";
}
log(LogLevel::Message, response);
print(devices[i].stateTopic, response);
break;
}
if (String(topic) == String(motorControlTopic) || String(topic) == String(motorSetTopic))
{
if (String(topic) == String(motorSetTopic))
{
//sets speed for now, other params later
linMotor.SetSpeed(messageTemp.toInt());
response = "Parameters set";
}
if (String(topic) == String(motorControlTopic))
{
if (messageTemp.toInt() > 0)
{
//check if motor is available to run
if (linMotor.getMotorStatus())
{
linMotor.MoveTo(messageTemp.toInt());
//TODO: Setup timer stuff here
// motorTimer = NULL;
// motorTimer = timerBegin(1, 80, true);
// timerAttachInterrupt(motorTimer, &motorInterrupt, true);
timerSetAutoReload(motorTimer, true);
timerAlarmWrite(motorTimer, 1, true);
timerAlarmEnable(motorTimer);
response = "moving to " + String(messageTemp.toInt()) + " mm position";
}
else
{
response = "motor is busy - wait for movement to end!";
}
}
else if (messageTemp == "home")
{
linMotor.SetZero();
response = "setting motor to zero";
}
else if (messageTemp == "stop")
{
linMotor.EStop();
if (motorTimer != NULL)
{
timerDetachInterrupt(motorTimer);
}
response = "motor stopped";
//TODO: detach timer here!
}
}
//TODO: put in GUI call for position updates
//print(motorStateTopic, "position is: " + String(linMotor.getPosition()));
log(LogLevel::Message, response);
print(motorStateTopic, response);
break;
}
}
}
void setup()
{
//Testing code here:
motorTimer = timerBegin(1, 80, true);
timerAttachInterrupt(motorTimer, &motorInterrupt, true);
//end testing code
//Start serial connection
Serial.begin(115200);
for (int i = 0; i < numDevices; i = i + 1)
{
pinMode(devices[i].pinNumber, OUTPUT);
digitalWrite(devices[i].pinNumber, devices[i].shouldInvert?HIGH:LOW);
}
pinMode(pulsePin, OUTPUT);
pinMode(directionPin, OUTPUT);
delay(500);
linMotor.SetSpeed(250);
linMotor.SetAcceleration(20);
log(LogLevel::Debug, "Connecting to mqtt");
Serial.println(mqtt_server);
setup_wifi();
client.setServer(mqtt_server, 1883);
client.setCallback(callback);
reconnect();
log(LogLevel::Message, "Connected");
log(LogLevel::Message, "System Started!");
// Initialize heartbeat timer
heartbeat_previousMillis = millis();
motorCheck_previousMillis = millis();
}
void reconnect()
{
// Loop until we're reconnected
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
Serial.println(mqtt_server);
// Attempt to connect
if (client.connect("ESP8266Client")) {
Serial.println("connected");
// Subscribe to all relevant messages
for (int i = 0; i < numDevices; i = i + 1)
{
client.subscribe(devices[i].controlTopic);
}
// subscribe to the heartbeat topic as well
client.subscribe(HeartbeatTopic);
client.subscribe(motorControlTopic);
client.subscribe(motorSetTopic);
}
else
{
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
// Wait 5 seconds before retrying
delay(5000);
}
}
}
void loop()
{
if (!client.connected()) reconnect();
client.loop();
unsigned long currentMillis = millis();
if (currentMillis - motorCheck_previousMillis >= motorCheckTime)
{
// print(motorStateTopic, "position is: " + String(linMotor.GetPosition()));
portENTER_CRITICAL(&timerMux);
Serial.println(String(linMotor.GetPosition()));
portEXIT_CRITICAL(&timerMux);
motorCheck_previousMillis = currentMillis;
if (finished)
{
// timerAlarmWrite(motorTimer, 0, false);
// Serial.println("wrote 0 alarm");
// timerAlarmDisable(motorTimer); // stop alarm
// Serial.println("disabled alarm");
// timerEnd(motorTimer);
// Serial.println("timerEnd");
// motorTimer = NULL;
// Serial.println("NULLED timer");
// motorTimer = timerBegin(1, 80, true);
// Serial.println("timer stated again!");
// timerRestart(motorTimer);
// timerDetachInterrupt(motorTimer); // detach interrupt
// timerEnd(motorTimer);
finished = false;
}
}
}
我认为 motor.h 方法不需要显示,但如果需要可以 post。在此先感谢您的帮助!
Edit1: 刚刚意识到在循环函数内部发生定时器删除之前互斥锁被关闭,但它仍然没有修复它。该部分评论中的内容也是我迄今为止尝试但未成功的所有内容。
Edit2:为清楚起见重新措辞。
'restart'这个词让我以为它会立即再次启动计时器,但事实证明并非如此。如果之前将重新加载设置为 false,则必须在计时器实际执行之前再次设置计时器 - 这非常适合我的用例。下面是我的新代码(我想我会包括 wifi 和 mqtt 的东西来帮助其他人):
#include <WiFi.h>
#include <PubSubClient.h>
#include <Wire.h>
#include "VCDevices.h"
hw_timer_t * motorTimer = NULL;
Motor linMotor2 = Motor(pulsePin, directionPin);
int d = 0;
bool nextRun = false;
const char* ssid = "SSID";
const char* password = "PASSWORD_HERE";
const char* mqtt_server = "MQTT_SERVER_HERE";
WiFiClient espClient;
PubSubClient client(espClient);
long lastMsg = 0;
char topArr[50];
char msgArr[100];
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
//Timer ISR
void IRAM_ATTR motorInterrupt(void)
{
portENTER_CRITICAL(&timerMux);
noInterrupts();
//check if the motor is in motion still
if (!linMotor2.getMotorStatus())
{
d = linMotor2.Update();
//give timer different delay, dependent on its current speed
timerAlarmWrite(motorTimer, d, true);
timerAlarmEnable(motorTimer);
}
//kill the timer and interrupt if not
else
{
nextRun = true;
//set the 'reload' boolean to false, to get it to only trigger one more time
timerAlarmWrite(motorTimer, 10, false);
// Serial.println("POSITION REACHED!");
}
interrupts();
portEXIT_CRITICAL(&timerMux);
}
void reconnect()
{
// Loop until we're reconnected
while (!client.connected())
{
Serial.print("Attempting MQTT connection...");
Serial.println(mqtt_server);
// Attempt to connect
if (client.connect("ESP8266Client"))
{
client.subscribe(motorControlTopic);
Serial.println("connected");
}
else
{
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
// Wait 5 seconds before retrying
delay(5000);
}
}
}
void setup_wifi()
{
delay(10);
// We start by connecting to a WiFi network
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}
void callback(char* topic, byte* message, unsigned int length)
{
Serial.print("Message arrived on topic: ");
Serial.print(topic);
Serial.print(". Message: ");
String messageTemp;
String response;
for (int i = 0; i < length; i++) {
Serial.print((char)message[i]);
messageTemp += (char)message[i];
}
Serial.println();
if (String(topic) == String(motorControlTopic))
{
if (messageTemp.toInt() > 0)
{
//check if motor is available to run
if (linMotor2.getMotorStatus())
{
linMotor2.MoveTo(messageTemp.toInt());
//set the motor timer and enable it
timerAlarmWrite(motorTimer, 1, true);
timerAlarmEnable(motorTimer);
response = "moving to " + String(messageTemp.toInt()) + " mm position";
}
else
{
response = "motor is busy - wait for movement to end!";
}
Serial.println(response);
}
}
}
void setup()
{
//Start serial connection
Serial.begin(115200);
//Setup wifi and mqtt stuff
setup_wifi();
client.setServer(mqtt_server, 1883);
client.setCallback(callback);
//Fix up motor settings
pinMode(pulsePin, OUTPUT);
linMotor2.SetSpeed(200);
linMotor2.SetAcceleration(10);
//Initialize timer here for later use!
motorTimer = timerBegin(1, 80, true);
timerAttachInterrupt(motorTimer, &motorInterrupt, true);
Serial.println("TIMER SET!!");
digitalWrite(pulsePin, LOW);
}
void loop()
{
if (!client.connected()) reconnect();
client.loop();
portENTER_CRITICAL(&timerMux);
vTaskDelay(500);
count = linMotor2.GetPosition();
Serial.println("POSITION: " + String(count));
if (nextRun)
{
noInterrupts();
timerRestart(motorTimer);
Serial.println("*********TIMER RESTARTED!******");
nextRun = false;
interrupts();
}
portEXIT_CRITICAL(&timerMux);
}
我发现这个问题与我遇到的问题非常相似,同样在步进应用程序中,我需要将步进器的引脚设置为 运行,然后在 2 毫秒后,需要设置引脚回到低电平。为此,我在第一个定时器的 ISR 中触发了第二个定时器,但无论我 try/set 是什么,第二个定时器总是在 23us 之后触发。为了说明我制作了下面的准系统示例,因此可以看到两个 ISR 之间的间隔无论如何始终为 22/23us。这个 routine/strategy 是非常流行的 TeensyStep library (ESP32 Fork) 的一部分,而且非常短的脉冲长度并没有被大驱动程序真正欣赏。我做错了什么?
hw_timer_t *timerA = NULL;
hw_timer_t *timerB = NULL;
void IRAM_ATTR onTimerA()
{
digitalWrite(13, 1);
Serial.print("HI ");
Serial.println(micros());
timerAlarmEnable(timerB);
}
void IRAM_ATTR onTimerB()
{
digitalWrite(13, 0);
Serial.print("LO ");
Serial.println(micros());
}
void setup()
{
Serial.begin(115200);
while (!Serial);
timerA = timerBegin(0, 80, true);
timerAttachInterrupt(timerA, &onTimerA, true);
timerAlarmWrite(timerA, 1000000, true);
timerB = timerBegin(1, 80, true);
timerAttachInterrupt(timerB, &onTimerB, true);
timerAlarmWrite(timerB, 200000, false);
timerAlarmEnable(timerA);
}
void loop(){}
我无法弄清楚如何禁用然后重新启用(在触发事件时)来自 esp-arduino 库的 hw (esp32-hal-timer) 定时器,here 用于步进电机我的 esp32 开发板的控制器应用程序。它会倒计时并根据需要多次触发 ISR,但是当我禁用它时(这样 ISR 就不会被不必要地调用),当我再次尝试启动它时它不会启动。奇怪的是它以与第一次相同的方式启动,所以我不确定这是我的代码问题还是特定库处理垃圾收集的方式。这也是我第一次尝试使用中断。我的代码如下。
为了避免费力太多,一般流程是在setup方法中初始化定时器(称为motorTimer),然后连接wifi,在mqtt的回调方法中对任何带有整数负载的消息将触发 motor.h class 中的 'moveTo' 方法,然后在触发 ISR 计时器时将触发同一个 class 中的更新方法。然后定时器将在每次迭代中更改其时间,以进行加速度补偿。这很好用,直到需要终止计时器然后稍后重新启动它 - 然后根本不会调用 ISR,就像计时器没有正确停止一样。这就是我的问题所在。
#include <SPI.h>
#include <Adafruit_MAX31855.h>
#include <WiFi.h>
#include <PubSubClient.h>
#include <Wire.h>
#include "VCDevices.h"
// Replace the next variables with your SSID/Password combination
// //WiFi info:
const char* ssid = "SSID";
const char* password = "PASSWORD_HERE";
// Add your MQTT Broker IP address, example:
const char* mqtt_server = "home.IOT.lan";
WiFiClient espClient;
PubSubClient client(espClient);
long lastMsg = 0;
char topArr[50];
char msgArr[100];
// get size of array first, to feed to for loop
int numDevices = sizeof(devices)/sizeof(device);
int value = 0;
unsigned long heartbeat_previousMillis = 0;
unsigned long motorCheck_previousMillis = 0;
const long timeOut = 60000;
const long motorCheckTime = 600;
hw_timer_t * motorTimer = NULL;
bool state = 0;
int count = 0;
int d = 0;
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
bool finished = false;
enum LogLevel
{
Debug, //Sends message only to the serial port
Error, //Sends message over MQTT to 'Errors' topic, and to serial port with "Error: " pre-appended
Message //Sends message over serial and MQTT to 'StatusMessage' topic
};
///****** TIMER LOGIC HERE ******
//TODO: timer needs to just figure out what time the next pulse needs to be fired at - needs to be calculated on the fly
//This is calculated inside the Motor class.
void IRAM_ATTR motorInterrupt(void)
{
Serial.println("B");
portENTER_CRITICAL(&timerMux);
noInterrupts();
//check if the motor is in motion still
if (!linMotor.getMotorStatus())
{
d = linMotor.Update();
timerAlarmWrite(motorTimer, d, true);
timerAlarmEnable(motorTimer);
}
else
{
// timerAlarmWrite(motorTimer, 1, true);
timerAlarmDisable(motorTimer);
finished = true;
}
//kill the timer and interrupt if not
interrupts();
portEXIT_CRITICAL(&timerMux);
}
//****** END TIMER HERE *****
void log(LogLevel level, String message)
{
switch(level)
{
case LogLevel::Debug:
Serial.println(message);
break;
case LogLevel::Error:
print(ErrorTopic, message);
break;
case LogLevel::Message:
Serial.print("Message: ");
Serial.println(message);
print(StatusTopic, message);
break;
}
}
void print(char topic[], String message)
{
Serial.print(topic);
Serial.print(" : ");
Serial.println(message);
//topic.toCharArray(topArr, sizeof(topic)+2);
message.toCharArray(msgArr, sizeof(msgArr));
client.publish(topic, msgArr, message.length());
}
// WiFi methods are located below:
void setup_wifi()
{
delay(10);
// We start by connecting to a WiFi network
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}
void callback(char* topic, byte* message, unsigned int length)
{
Serial.print("Message arrived on topic: ");
Serial.print(topic);
Serial.print(". Message: ");
String messageTemp;
String response;
//check if heartbeat signal was sent
if (String(topic) == HeartbeatTopic)
{
heartbeat_previousMillis = millis();
}
for (int i = 0; i < length; i++) {
Serial.print((char)message[i]);
messageTemp += (char)message[i];
}
Serial.println();
// Iterate through each device and update states accordingly
for (int i = 0; i < numDevices; i = i + 1)
{
// the char arrays need to be cast as strings to compare to each other
if (String(topic) == String(devices[i].controlTopic))
{
if (messageTemp == "on")
{
digitalWrite(devices[i].pinNumber, devices[i].shouldInvert?LOW:HIGH);
response = "on";
}
else
{
digitalWrite(devices[i].pinNumber, devices[i].shouldInvert?HIGH:LOW);
response = "off";
}
log(LogLevel::Message, response);
print(devices[i].stateTopic, response);
break;
}
if (String(topic) == String(motorControlTopic) || String(topic) == String(motorSetTopic))
{
if (String(topic) == String(motorSetTopic))
{
//sets speed for now, other params later
linMotor.SetSpeed(messageTemp.toInt());
response = "Parameters set";
}
if (String(topic) == String(motorControlTopic))
{
if (messageTemp.toInt() > 0)
{
//check if motor is available to run
if (linMotor.getMotorStatus())
{
linMotor.MoveTo(messageTemp.toInt());
//TODO: Setup timer stuff here
// motorTimer = NULL;
// motorTimer = timerBegin(1, 80, true);
// timerAttachInterrupt(motorTimer, &motorInterrupt, true);
timerSetAutoReload(motorTimer, true);
timerAlarmWrite(motorTimer, 1, true);
timerAlarmEnable(motorTimer);
response = "moving to " + String(messageTemp.toInt()) + " mm position";
}
else
{
response = "motor is busy - wait for movement to end!";
}
}
else if (messageTemp == "home")
{
linMotor.SetZero();
response = "setting motor to zero";
}
else if (messageTemp == "stop")
{
linMotor.EStop();
if (motorTimer != NULL)
{
timerDetachInterrupt(motorTimer);
}
response = "motor stopped";
//TODO: detach timer here!
}
}
//TODO: put in GUI call for position updates
//print(motorStateTopic, "position is: " + String(linMotor.getPosition()));
log(LogLevel::Message, response);
print(motorStateTopic, response);
break;
}
}
}
void setup()
{
//Testing code here:
motorTimer = timerBegin(1, 80, true);
timerAttachInterrupt(motorTimer, &motorInterrupt, true);
//end testing code
//Start serial connection
Serial.begin(115200);
for (int i = 0; i < numDevices; i = i + 1)
{
pinMode(devices[i].pinNumber, OUTPUT);
digitalWrite(devices[i].pinNumber, devices[i].shouldInvert?HIGH:LOW);
}
pinMode(pulsePin, OUTPUT);
pinMode(directionPin, OUTPUT);
delay(500);
linMotor.SetSpeed(250);
linMotor.SetAcceleration(20);
log(LogLevel::Debug, "Connecting to mqtt");
Serial.println(mqtt_server);
setup_wifi();
client.setServer(mqtt_server, 1883);
client.setCallback(callback);
reconnect();
log(LogLevel::Message, "Connected");
log(LogLevel::Message, "System Started!");
// Initialize heartbeat timer
heartbeat_previousMillis = millis();
motorCheck_previousMillis = millis();
}
void reconnect()
{
// Loop until we're reconnected
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
Serial.println(mqtt_server);
// Attempt to connect
if (client.connect("ESP8266Client")) {
Serial.println("connected");
// Subscribe to all relevant messages
for (int i = 0; i < numDevices; i = i + 1)
{
client.subscribe(devices[i].controlTopic);
}
// subscribe to the heartbeat topic as well
client.subscribe(HeartbeatTopic);
client.subscribe(motorControlTopic);
client.subscribe(motorSetTopic);
}
else
{
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
// Wait 5 seconds before retrying
delay(5000);
}
}
}
void loop()
{
if (!client.connected()) reconnect();
client.loop();
unsigned long currentMillis = millis();
if (currentMillis - motorCheck_previousMillis >= motorCheckTime)
{
// print(motorStateTopic, "position is: " + String(linMotor.GetPosition()));
portENTER_CRITICAL(&timerMux);
Serial.println(String(linMotor.GetPosition()));
portEXIT_CRITICAL(&timerMux);
motorCheck_previousMillis = currentMillis;
if (finished)
{
// timerAlarmWrite(motorTimer, 0, false);
// Serial.println("wrote 0 alarm");
// timerAlarmDisable(motorTimer); // stop alarm
// Serial.println("disabled alarm");
// timerEnd(motorTimer);
// Serial.println("timerEnd");
// motorTimer = NULL;
// Serial.println("NULLED timer");
// motorTimer = timerBegin(1, 80, true);
// Serial.println("timer stated again!");
// timerRestart(motorTimer);
// timerDetachInterrupt(motorTimer); // detach interrupt
// timerEnd(motorTimer);
finished = false;
}
}
}
我认为 motor.h 方法不需要显示,但如果需要可以 post。在此先感谢您的帮助!
Edit1: 刚刚意识到在循环函数内部发生定时器删除之前互斥锁被关闭,但它仍然没有修复它。该部分评论中的内容也是我迄今为止尝试但未成功的所有内容。
Edit2:为清楚起见重新措辞。
'restart'这个词让我以为它会立即再次启动计时器,但事实证明并非如此。如果之前将重新加载设置为 false,则必须在计时器实际执行之前再次设置计时器 - 这非常适合我的用例。下面是我的新代码(我想我会包括 wifi 和 mqtt 的东西来帮助其他人):
#include <WiFi.h>
#include <PubSubClient.h>
#include <Wire.h>
#include "VCDevices.h"
hw_timer_t * motorTimer = NULL;
Motor linMotor2 = Motor(pulsePin, directionPin);
int d = 0;
bool nextRun = false;
const char* ssid = "SSID";
const char* password = "PASSWORD_HERE";
const char* mqtt_server = "MQTT_SERVER_HERE";
WiFiClient espClient;
PubSubClient client(espClient);
long lastMsg = 0;
char topArr[50];
char msgArr[100];
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
//Timer ISR
void IRAM_ATTR motorInterrupt(void)
{
portENTER_CRITICAL(&timerMux);
noInterrupts();
//check if the motor is in motion still
if (!linMotor2.getMotorStatus())
{
d = linMotor2.Update();
//give timer different delay, dependent on its current speed
timerAlarmWrite(motorTimer, d, true);
timerAlarmEnable(motorTimer);
}
//kill the timer and interrupt if not
else
{
nextRun = true;
//set the 'reload' boolean to false, to get it to only trigger one more time
timerAlarmWrite(motorTimer, 10, false);
// Serial.println("POSITION REACHED!");
}
interrupts();
portEXIT_CRITICAL(&timerMux);
}
void reconnect()
{
// Loop until we're reconnected
while (!client.connected())
{
Serial.print("Attempting MQTT connection...");
Serial.println(mqtt_server);
// Attempt to connect
if (client.connect("ESP8266Client"))
{
client.subscribe(motorControlTopic);
Serial.println("connected");
}
else
{
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
// Wait 5 seconds before retrying
delay(5000);
}
}
}
void setup_wifi()
{
delay(10);
// We start by connecting to a WiFi network
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}
void callback(char* topic, byte* message, unsigned int length)
{
Serial.print("Message arrived on topic: ");
Serial.print(topic);
Serial.print(". Message: ");
String messageTemp;
String response;
for (int i = 0; i < length; i++) {
Serial.print((char)message[i]);
messageTemp += (char)message[i];
}
Serial.println();
if (String(topic) == String(motorControlTopic))
{
if (messageTemp.toInt() > 0)
{
//check if motor is available to run
if (linMotor2.getMotorStatus())
{
linMotor2.MoveTo(messageTemp.toInt());
//set the motor timer and enable it
timerAlarmWrite(motorTimer, 1, true);
timerAlarmEnable(motorTimer);
response = "moving to " + String(messageTemp.toInt()) + " mm position";
}
else
{
response = "motor is busy - wait for movement to end!";
}
Serial.println(response);
}
}
}
void setup()
{
//Start serial connection
Serial.begin(115200);
//Setup wifi and mqtt stuff
setup_wifi();
client.setServer(mqtt_server, 1883);
client.setCallback(callback);
//Fix up motor settings
pinMode(pulsePin, OUTPUT);
linMotor2.SetSpeed(200);
linMotor2.SetAcceleration(10);
//Initialize timer here for later use!
motorTimer = timerBegin(1, 80, true);
timerAttachInterrupt(motorTimer, &motorInterrupt, true);
Serial.println("TIMER SET!!");
digitalWrite(pulsePin, LOW);
}
void loop()
{
if (!client.connected()) reconnect();
client.loop();
portENTER_CRITICAL(&timerMux);
vTaskDelay(500);
count = linMotor2.GetPosition();
Serial.println("POSITION: " + String(count));
if (nextRun)
{
noInterrupts();
timerRestart(motorTimer);
Serial.println("*********TIMER RESTARTED!******");
nextRun = false;
interrupts();
}
portEXIT_CRITICAL(&timerMux);
}
我发现这个问题与我遇到的问题非常相似,同样在步进应用程序中,我需要将步进器的引脚设置为 运行,然后在 2 毫秒后,需要设置引脚回到低电平。为此,我在第一个定时器的 ISR 中触发了第二个定时器,但无论我 try/set 是什么,第二个定时器总是在 23us 之后触发。为了说明我制作了下面的准系统示例,因此可以看到两个 ISR 之间的间隔无论如何始终为 22/23us。这个 routine/strategy 是非常流行的 TeensyStep library (ESP32 Fork) 的一部分,而且非常短的脉冲长度并没有被大驱动程序真正欣赏。我做错了什么?
hw_timer_t *timerA = NULL;
hw_timer_t *timerB = NULL;
void IRAM_ATTR onTimerA()
{
digitalWrite(13, 1);
Serial.print("HI ");
Serial.println(micros());
timerAlarmEnable(timerB);
}
void IRAM_ATTR onTimerB()
{
digitalWrite(13, 0);
Serial.print("LO ");
Serial.println(micros());
}
void setup()
{
Serial.begin(115200);
while (!Serial);
timerA = timerBegin(0, 80, true);
timerAttachInterrupt(timerA, &onTimerA, true);
timerAlarmWrite(timerA, 1000000, true);
timerB = timerBegin(1, 80, true);
timerAttachInterrupt(timerB, &onTimerB, true);
timerAlarmWrite(timerB, 200000, false);
timerAlarmEnable(timerA);
}
void loop(){}