// ПР8. USART: ВЕДОМЫЙ микроконтроллер (исполнитель команд)
// Плата: NUCLEO-C031C6, Wokwi + STM32duino
// Автор: Алексеев Ф.В., ЭФБО-08-23
//
// Линия связи: USART1, RX = PB7 (D0), TX = PB6 (D1), 115200 бод, 8N1
// Протокол (ASCII, команда заканчивается ';' или '?'):
// SERV:090; — сервопривод в 90° (0…180)
// STEP:FWD,200; — шаговый двигатель, 200 шагов вперёд (1…2000)
// STEP:REV,100; — 100 шагов назад
// STATUS? — запрос состояния
// Ответы: OK, ERROR:FORMAT, ERROR:RANGE, ERROR:CMD, ERROR:BUSY, ERROR:LEN, STATUS ...
// Исполнительные устройства: сервопривод — TIM3_CH2 (PB5, D6), 50 Гц;
// A4988: STEP = D3, DIR = D2 (200 шагов/оборот)
HardwareSerial Link(PB7, PB6); // USART1
#define SERVO_PIN D6
#define STEP_PIN D3
#define DIR_PIN D2
HardwareTimer *servoTim;
uint32_t servoCh;
int servoAngle = 90;
long position = 0, stepsLeft = 0;
int dirSign = 1;
uint32_t tStep = 0;
const uint32_t STEP_US = 2000; // 500 шагов/с (150 об/мин)
char buf[24]; // буфер принимаемой команды
uint8_t len = 0;
bool overflow = false;
void servoWrite(int a) {
servoAngle = a;
servoTim->setCaptureCompare(servoCh, map(a, 0, 180, 544, 2400), TICK_COMPARE_FORMAT);
}
void reply(const char *s) { Link.print(s); Link.print("\r\n"); }
// Разбор целого числа из 1…4 цифр; возвращает -1 при ошибке формата
long parseNum(const char *s) {
if (*s == 0) return -1;
long v = 0; uint8_t n = 0;
while (*s) {
if (*s < '0' || *s > '9' || ++n > 4) return -1;
v = v * 10 + (*s++ - '0');
}
return v;
}
void execute(char *cmd) {
if (strcmp(cmd, "STATUS?") == 0) {
char out[48];
snprintf(out, sizeof(out), "STATUS SERV=%03d POS=%+05ld BUSY=%d",
servoAngle, position, stepsLeft ? 1 : 0);
reply(out);
} else if (strncmp(cmd, "SERV:", 5) == 0) {
long a = parseNum(cmd + 5);
if (a < 0) reply("ERROR:FORMAT");
else if (a > 180) reply("ERROR:RANGE");
else { servoWrite(a); reply("OK"); }
} else if (strncmp(cmd, "STEP:", 5) == 0) {
char *p = cmd + 5;
int d = 0;
if (strncmp(p, "FWD,", 4) == 0) d = 1;
else if (strncmp(p, "REV,", 4) == 0) d = -1;
long n = d ? parseNum(p + 4) : -1;
if (n < 0) reply("ERROR:FORMAT");
else if (n < 1 || n > 2000) reply("ERROR:RANGE");
else if (stepsLeft) reply("ERROR:BUSY");
else { // OK будет отправлен по завершении шагов
dirSign = d; digitalWrite(DIR_PIN, d > 0); stepsLeft = n;
}
} else {
reply("ERROR:CMD");
}
}
// Приём символов: команда заканчивается ';' (или '?' для STATUS)
void receive() {
while (Link.available()) {
char c = Link.read();
if (c == '\r' || c == '\n') { if (len == 0) continue; c = ';'; }
if (c == ';' || c == '?') {
if (c == '?' && len < sizeof(buf) - 1) buf[len++] = '?';
buf[len] = 0;
if (overflow) reply("ERROR:LEN");
else if (len) execute(buf);
len = 0; overflow = false;
} else if (len < sizeof(buf) - 2) {
buf[len++] = c;
} else {
overflow = true; // слишком длинная команда
}
}
}
void stepperRun() {
if (!stepsLeft || micros() - tStep < STEP_US) return;
tStep = micros();
digitalWrite(STEP_PIN, HIGH); delayMicroseconds(3); digitalWrite(STEP_PIN, LOW);
position += dirSign;
if (--stepsLeft == 0) reply("OK"); // подтверждение выполнения
}
void setup() {
Link.begin(115200, SERIAL_8N1);
pinMode(STEP_PIN, OUTPUT); pinMode(DIR_PIN, OUTPUT);
PinName pn = digitalPinToPinName(SERVO_PIN);
servoTim = new HardwareTimer((TIM_TypeDef *)pinmap_peripheral(pn, PinMap_PWM));
servoCh = STM_PIN_CHANNEL(pinmap_function(pn, PinMap_PWM));
servoTim->setMode(servoCh, TIMER_OUTPUT_COMPARE_PWM1, pn);
servoTim->setPrescaleFactor(48); // 1 МГц
servoTim->setOverflow(20000, TICK_FORMAT); // 20 мс
servoWrite(90);
servoTim->resume();
reply("SLAVE READY");
}
void loop() {
receive();
stepperRun();
}
Loading
st-nucleo-c031c6
st-nucleo-c031c6