#include "C:\Zendion Inc\agrobot_base\Firmware\Modulos\SerialService.h" #define D_Code Mov int PinoReleGeral = 23; bool Conectado = false; int _TaxaAmostragem; bool M1_Ativado = false; bool M2_Ativado = false; bool M3_Ativado = false; bool M4_Ativado = false; int ZeroRampa = 0; int PotenciaMax = 255; class Motor { public: // Construtor Motor(String desc, int canal, int _pwm, int _vel, int _dir, int _brk, int _stp) { _descricao = desc; _canal = canal; _pinoPWM = _pwm; _pinoVEL = _vel; _pinoDIR = _dir; _pinoBRK = _brk; _pinoSTP = _stp; } // Definições String _descricao; int _canal; int _pinoPWM; int _pinoVEL; int _pinoDIR; int _pinoBRK; int _pinoSTP; bool Iniciado = false; bool Testando = false; // Consumo double RPM; int PotenciaAtual; volatile bool pulso_hall = false; volatile unsigned long tempoAnterior = 0; volatile unsigned long periodo = 0; // Entrada de dados bool _FOC; int _Rampa; int _Precisao; double _Potencia; double _RPM_SP; Sentido _Sentido = Parado; Sentido _SentidoA = Parado; bool _RampaAtivada = false; int _Margem = 2; void Inicializar(bool _foc) { RPM = 0; pulso_hall = false; periodo = 0; tempoAnterior = 0; _FOC = _foc; if (Iniciado) { EnviarDadosSerial(_descricao + " ja inicializado"); return; } pinMode(_pinoPWM, OUTPUT); pinMode(_pinoVEL, INPUT); pinMode(_pinoDIR, OUTPUT); pinMode(_pinoBRK, OUTPUT); pinMode(_pinoSTP, OUTPUT); digitalWrite(_pinoPWM, LOW); digitalWrite(_pinoDIR, HIGH); digitalWrite(_pinoBRK, LOW); digitalWrite(_pinoSTP, LOW); ledcSetup(_canal, 1000, 10); //ledcAttachPin(_pinoPWM, _canal); if (_canal == 0) { attachInterrupt(digitalPinToInterrupt(_pinoVEL), contarPulsoM1, RISING); } else if (_canal == 1) { attachInterrupt(digitalPinToInterrupt(_pinoVEL), contarPulsoM2, RISING); } else if (_canal == 2) { attachInterrupt(digitalPinToInterrupt(_pinoVEL), contarPulsoM3, RISING); } else if (_canal == 3) { attachInterrupt(digitalPinToInterrupt(_pinoVEL), contarPulsoM4, RISING); } xTaskCreatePinnedToCore(&Motor::RPMTaskWrapper, "RPMTask", 4000, this, _canal, &RPMTaskHandle, 0); xTaskCreatePinnedToCore(&Motor::RampaTaskWrapper, "RampaTask", 4000, this, 20 - _canal, &RampaTaskHandle, 0); EnviarDadosSerial(_descricao + " Iniciado"); Iniciado = true; EnviarDadosSerial(MontarProtocoloSensor(sCfg, _descricao, Iniciado ? "1" : "0")); } void Desligar() { if (!Iniciado) { EnviarDadosSerial(_descricao + " nao esta inicializado"); return; } // Parar a execução das tarefas vTaskDelete(RPMTaskHandle); vTaskDelete(RampaTaskHandle); // Desanexar o canal PWM //ledcDetachPin(_pinoPWM); // Redefinir as configurações para os valores iniciais pinMode(_pinoPWM, INPUT); pinMode(_pinoVEL, INPUT); pinMode(_pinoDIR, INPUT); pinMode(_pinoBRK, INPUT); pinMode(_pinoSTP, INPUT); // Outras redefinições de variáveis de estado, se necessário RPM = 0; periodo = 0; tempoAnterior = 0; pulso_hall = false; EnviarDadosSerial(_descricao + " Desligado"); Iniciado = false; EnviarDadosSerial(MontarProtocoloSensor(sCfg, _descricao, Iniciado ? "1" : "0")); } void Testar() { Testando = true; EnviarDadosSerial(MontarProtocoloSensor(sTST, _descricao, "Testando motor " + _descricao + "...")); TestePino("DIR", _pinoDIR); TestePino("BRK", _pinoBRK); TestePino("STP", _pinoSTP); TestePWM(); //TesteAcionamentoMotores(); Testando = false; EnviarDadosSerial(MontarProtocoloSensor(sTST, _descricao, "Teste finalizado")); } void TestePino(String Descricao, int Pino) { EnviarDadosSerial("Pino " + ((String)Pino) + ": " + Descricao); digitalWrite(Pino, LOW); vTaskDelay(100); digitalWrite(Pino, HIGH); vTaskDelay(100); int Estado = digitalRead(Pino); EnviarDadosSerial(MontarProtocoloSensor(sTST, _descricao, MontarDadosProtocoloTeste(Testando, Descricao, ((String)((Estado == 1) ? "SUCESSO" : "FALHA")), "1", ((String)Estado)))); vTaskDelay(1000); digitalWrite(Pino, LOW); vTaskDelay(100); Estado = digitalRead(Pino); EnviarDadosSerial(MontarProtocoloSensor(sTST, _descricao, MontarDadosProtocoloTeste(Testando, Descricao, ((String)((Estado == 0) ? "SUCESSO" : "FALHA")), "0", ((String)Estado)))); vTaskDelay(1000); } void TestePWM() { EnviarDadosSerial("Pino " + ((String)_pinoPWM) + ": Canal " + ((String)_canal)); AtualizaTestePWM(0); AtualizaTestePWM(255); AtualizaTestePWM(512); AtualizaTestePWM(768); AtualizaTestePWM(1024); } void AtualizaTestePWM(int Comando) { ledcWrite(_canal, Comando); vTaskDelay(100); int Leitura = analogRead(_pinoPWM); EnviarDadosSerial(MontarProtocoloSensor(sTST, _descricao, MontarDadosProtocoloTeste(Testando, "PWM", (Leitura == Comando ? "SUCESSO" : "FALHA"), (String)Comando, (String)Leitura))); vTaskDelay(500); } void TesteAcionamentoMotores() { EnviarDadosSerial("Sentido Horario"); PotenciaAtual = 20; _Sentido = Horario; Atualizar(); vTaskDelay(2000); EnviarDadosSerial("Sentido Parado"); _Sentido = Parado; PotenciaAtual = 0; Atualizar(); vTaskDelay(2000); EnviarDadosSerial("Sentido Antihorario"); _Sentido = Antihorario; PotenciaAtual = 20; Atualizar(); vTaskDelay(2000); EnviarDadosSerial("Sentido Parado"); _Sentido = Parado; PotenciaAtual = 0; Atualizar(); vTaskDelay(2000); } void Atualizar() { digitalWrite(_pinoDIR, _SentidoA == Horario); digitalWrite(_pinoSTP, _SentidoA != Parado); //ledcWrite(_canal, PotenciaAtual); dacWrite(_pinoPWM, PotenciaAtual); } private: static Motor* instanceM1; static Motor* instanceM2; static Motor* instanceM3; static Motor* instanceM4; static void contarPulsoM1() { instanceM1->pulso_hall = true; } static void contarPulsoM2() { instanceM2->pulso_hall = true; } static void contarPulsoM3() { instanceM3->pulso_hall = true; } static void contarPulsoM4() { instanceM4->pulso_hall = true; } TaskHandle_t RPMTaskHandle = NULL; static void RPMTaskWrapper(void *pvParameters) { Motor *motor = static_cast(pvParameters); motor->RPMTask(); } void RPMTask() { unsigned long firstMilis = millis(); while (1) { // Medir RPM apenas quando ocorrer um pulso if (pulso_hall) { // Calcular o período em microssegundos unsigned long tempoAtual = micros(); periodo = tempoAtual - tempoAnterior; double RPM_A = RPM; // Calcular RPM if (periodo != 0) { RPM = 4000000 / (periodo); // 15 pulsos por revolução } else { RPM = 0; // Lidar com divisão por zero } if (RPM >= 800) { // Tratar ponto de parada RPM = RPM_A; } // Reiniciar variáveis tempoAnterior = tempoAtual; pulso_hall = false; } long intMillis = (millis() - firstMilis); if (intMillis >= _TaxaAmostragem) { EnviarDadosSerial(MontarProtocoloSensor(sRPM, _descricao, MontarDadosProtocoloRPM(RPM, _Potencia, periodo))); firstMilis = millis(); CorrigePotenciaMotor(); } vTaskDelay(1); } } void CorrigePotenciaMotor() { if (_FOC && (((RPM + _Margem) < _RPM_SP) || ((RPM - _Margem) > _RPM_SP))) { int PA = PotenciaAcrescentar(); _Potencia += PA; if (_Potencia > 100) { _Potencia = 100; } else if (_Potencia < 1) { _Potencia = 1; } _RampaAtivada = true; } } int PotenciaAcrescentar() { if (((RPM + _Margem) > _RPM_SP) && ((RPM - _Margem) < _RPM_SP)) { return 0; } float PercentualDistancia = (_RPM_SP / (RPM == 0 ? 1 : RPM)); float FatorDivisor = 1; // (_TaxaAmostragem / 1000) < 1 ? 1 : (_TaxaAmostragem / 1000); float AcrescimoPotencia = (PercentualDistancia * _Potencia) - _Potencia; float AcrescimoPotenciaCorrigido = round(AcrescimoPotencia / FatorDivisor); return AcrescimoPotenciaCorrigido; } //1000;010;2;070;004;005 //1000;010;0;070;004;005 TaskHandle_t RampaTaskHandle = NULL; static void RampaTaskWrapper(void *pvParameters) { Motor *motor = static_cast(pvParameters); motor->RampaTask(); } void RampaTask() { unsigned long previousMillisRampa = millis(); double MultiplicadorPotencia = PotenciaMax / 100; while (1) { unsigned long currentMillis = millis(); if (currentMillis - previousMillisRampa >= _Rampa && _RampaAtivada) { previousMillisRampa = currentMillis; bool Limite = false; if ((_Sentido == Parado || _Sentido != _SentidoA) && PotenciaAtual > ZeroRampa) { // M1 estiver parando ou invertendo, e pwm for maior zero rampa PotenciaAtual -= _Precisao; // Decrementar para atingir ponto zero rampa if (PotenciaAtual < ZeroRampa) PotenciaAtual = ZeroRampa; } else if (_Sentido != _SentidoA && PotenciaAtual == ZeroRampa) { // ZEROU AO MUDAR SENTIDO DE GIRO ANTES DE AUMENTAR _SentidoA = _Sentido; } else if (_Sentido != Parado && PotenciaAtual < (_Potencia * MultiplicadorPotencia)) { // abaixo do set point PotenciaAtual += _Precisao; // Incrementar para atingir o set point if (PotenciaAtual > PotenciaMax) PotenciaAtual = PotenciaMax; } else { // Estabiliza a potência do motor if (_Sentido == Parado) { PotenciaAtual = ZeroRampa; _Potencia = PotenciaAtual; } else { PotenciaAtual = (PotenciaAtual > PotenciaMax) ? PotenciaMax : (_Potencia * MultiplicadorPotencia); } _SentidoA = _Sentido; Limite = true; } Atualizar(); if (Limite) { _RampaAtivada = false; } } vTaskDelay(1); } } }; Motor M1("ET", 0, 27, 35, 12, 00, 22); Motor M2("EF", 1, 14, 34, 33, 00, 32); Motor M3("DT", 2, 26, 39, 02, 00, 04); Motor M4("DF", 3, 05, 36, 18, 00, 21); Motor* Motor::instanceM1 = &M1; Motor* Motor::instanceM2 = &M2; Motor* Motor::instanceM3 = &M3; Motor* Motor::instanceM4 = &M4; void setup() { Serial.begin(115200); pinMode(PinoReleGeral, OUTPUT); digitalWrite(PinoReleGeral, LOW); } void loop() { if (Serial.available() > 3) { //Serial.println(); String Protocolo = ""; //1000;080;1;020;001 F_Code _funcao = Nda; while (Serial.available()) { char Entrada = (char)Serial.read(); //Serial.print(Entrada); if (Entrada == EndLine) { break; } Protocolo += Entrada; if (Protocolo.length() == 3) { if (_funcao == Nda) { _funcao = (F_Code)((String)Protocolo[0] + (String)Protocolo[1] + (String)Protocolo[2]).toInt(); Protocolo = ""; } } } if (_funcao == Chk) { EnviarDadosSerial(MontarProtocoloVerificacao(D_Code)); } else if (_funcao == Cfg) { Conectado = (String)Protocolo[0] == "1"; _TaxaAmostragem = ((String)Protocolo[2] + (String)Protocolo[3] + (String)Protocolo[4] + (String)Protocolo[5] + (String)Protocolo[6]).toInt(); M1_Ativado = (String)Protocolo[8] == "1"; M2_Ativado = (String)Protocolo[10] == "1"; M3_Ativado = (String)Protocolo[12] == "1"; M4_Ativado = (String)Protocolo[14] == "1"; bool _foc = (String)Protocolo[16] == "1"; if (M1_Ativado) { if (Conectado) { M1.Inicializar(_foc); } else { M1.Desligar(); } vTaskDelay(500); } if (M2_Ativado) { if (Conectado) { M2.Inicializar(_foc); } else { M2.Desligar(); } vTaskDelay(500); } if (M3_Ativado) { if (Conectado) { M3.Inicializar(_foc); } else { M3.Desligar(); } vTaskDelay(500); } if (M4_Ativado) { if (Conectado) { M4.Inicializar(_foc); } else { M4.Desligar(); } vTaskDelay(500); } digitalWrite(PinoReleGeral, Conectado); vTaskDelay(100); EnviarDadosSerial(MontarProtocoloSensor(sCfg, "MOD", Conectado ? "1" : "0")); } else if (_funcao == Cmd) { //canal;potencia;sentido;rampa;precisao;rpm //0;000;0;000;000;000 int Canal = ((String)Protocolo[0]).toInt(); Motor* _motor = Canal == 0 ? &M1 : Canal == 1 ? &M2 : Canal == 2 ? &M3 : &M4; _motor->_Potencia = ((String)Protocolo[2] + (String)Protocolo[3] + (String)Protocolo[4]).toInt(); _motor->_Sentido = (Sentido)((String)Protocolo[6]).toInt(); _motor->_Rampa = ((String)Protocolo[8] + (String)Protocolo[9] + (String)Protocolo[10]).toInt(); _motor->_Precisao = ((String)Protocolo[12] + (String)Protocolo[13] + (String)Protocolo[14]).toInt(); _motor->_RPM_SP = ((String)Protocolo[16] + (String)Protocolo[17] + (String)Protocolo[18]).toInt(); bool _foc = (String)Protocolo[20] == "1"; _motor->_FOC = _foc; _motor->_RampaAtivada = true; } else if (_funcao == Tst) { int Canal = ((String)Protocolo[0]).toInt(); Motor* _motor = Canal == 0 ? &M1 : Canal == 1 ? &M2 : Canal == 2 ? &M3 : &M4; bool EmTeste = _motor->Testando; if (!EmTeste) { _motor->Testar(); } } } delay(10); }