如何解决如何根据需要为esp32 arduino步进电机控制器应用程序删除并重新启动硬件计时器用于中断
我在弄清楚如何禁用然后重新启用(在触发事件时)esp-arduino库here中的硬件(esp32-hal-timer)计时器(对于步进电机控制器应用程序)时遇到麻烦与我的esp32开发板。它会递减计数并根据需要触发ISR,但会禁用它(这样就不会不必要地调用ISR)了,但是当我尝试重新启动它时,它不会启动。奇怪的是,它的启动方式与第一次相同,所以我不确定这是否与我的代码有关,或者与特定库处理垃圾收集的方式有关。这也是我第一次尝试使用中断。我的代码在下面。
为避免麻烦,一般过程是在设置方法中初始化计时器(称为motorTimer),然后连接到wifi,在mqtt的回调方法中,任何带有整数有效载荷的消息都会触发然后在motor.h类中使用“ moveTo”方法,然后在触发ISR计时器时触发同一类中的update方法。然后,计时器将在每次迭代中更改其时间,以进行加速度补偿。这很有效,直到需要杀死计时器然后稍后重新启动-才完全不调用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,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,true);
timerAttachInterrupt(motorTimer,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,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,true);
// Serial.println("timer stated again!");
// timerRestart(motorTimer);
// timerDetachInterrupt(motorTimer); // detach interrupt
// timerEnd(motorTimer);
finished = false;
}
}
}
我不认为需要显示motor.h方法,但是如果需要可以发布。预先感谢您的帮助!
Edit1:只是意识到互斥体在循环功能内的计时器删除发生之前已被关闭,但是它仍然没有解决。同样,该部分评论中的内容是我迄今为止尝试的所有未成功的内容。
Edit2:为清楚起见,重新措辞。
解决方法
“重新启动”一词让我想到它将立即立即重新启动计时器,但事实并非如此。如果之前将重载设置为false,则必须在实际执行之前重新设置计时器-这对于我的用例来说非常合适。下面是我的新代码(图上我将包括wifi和mqtt内容,以帮助其他人):
library(dplyr)
join_then_average <- function(df1,df2,var) {
full_join(df1,by = "id") %>%
mutate(x = rowMeans(select(.,contains(var))))
}
join_then_average(a,b,'x')
# A tibble: 3 x 4
# id x.x x.y x
# <int> <int> <int> <dbl>
#1 1 4 16 10
#2 2 5 17 11
#3 3 6 18 12
,
我发现这个问题与我遇到的问题非常相似,同样在步进应用程序中,我需要为步进器设置一个引脚高电平才能运行,然后在 2 毫秒后,需要将引脚设置回低电平。为此,我在第一个计时器的 ISR 内触发了第二个计时器表单,但无论我尝试/设置什么,第二个计时器总是在 23us 后触发。为了说明我制作了下面的准系统示例,以便可以看到两个 ISR 之间的间隔无论如何总是 22/23us。此例程/策略是非常流行的 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,true);
timerAttachInterrupt(timerB,&onTimerB,true);
timerAlarmWrite(timerB,200000,false);
timerAlarmEnable(timerA);
}
void loop(){}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。