gpt4 book ai didi

timer - 如何按需删除和重启 esp32 arduino(步进电机 Controller 应用程序)的硬件定时器(用于中断)

转载 作者:行者123 更新时间:2023-12-05 04:57:02 26 4
gpt4 key购买 nike

我无法弄清楚如何禁用然后重新启用(在触发事件时)来自 esp-arduino 库的 hw (esp32-hal-timer) 计时器,here用于我的 esp32 开发板的步进电机 Controller 应用程序。它会倒计时并根据需要多次触发 ISR,但是当我禁用它时(这样 ISR 就不会被不必要地调用),当我再次尝试启动它时它不会启动。奇怪的是它以与第一次相同的方式启动,所以我不确定这是我的代码问题还是特定库处理垃圾收集的方式。这也是我第一次尝试使用中断。我的代码如下。

为了避免折腾太多,一般流程是在setup方法中初始化timer(称为motorTimer),然后连接wifi,在mqtt的回调方法中,任何payload为整数的消息都会触发motor.h 类中的“moveTo”方法,然后在触发 ISR 计时器时将触发同一类中的更新方法。然后定时器将在每次迭代中更改其时间,以进行加速度补偿。这很好用,直到需要终止计时器然后稍后重新启动它 - 然后根本不会调用 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 方法,但如果需要可以发布。在此先感谢您的帮助!

Edit1:刚刚意识到在循环函数内部发生定时器删除之前互斥量已关闭,但它仍然没有修复它。该部分评论中的内容也是我迄今为止尝试但未成功的所有内容。

Edit2:为清楚起见重新措辞。

最佳答案

“重启”这个词让我以为它会立即再次启动计时器,但事实并非如此。如果之前将重新加载设置为 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);
}

关于timer - 如何按需删除和重启 esp32 arduino(步进电机 Controller 应用程序)的硬件定时器(用于中断),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64657661/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com