управление по UART 9600
команды:
? - вывести список комманд
?? вывести названия регистров ATMEGA328 и адреса
boot- быстро мигнуть 3 раза светодиодом и через 3 секунды перейти в бутлоадер
boot5 - быстро мигнуть 5 раз и через 5 секунд перейти в бутлоадер
rd адрес - считать ячейку памяти
rdb адрес, номер бита - считать бит по адресу
erd адрес - считать ячейку EEPROM
erdb адрес, номер бита - считать бит из EEPROM по адресу
wr адрес данные - записать в ячейку данные
ewr адрес данные - записать в eeprom
clre - очистить EEPROM
wrsb адрес,номер бита- установить бит по адресу
wrrb адрес,номер бита- сбросить бит по адресу
wrib адрес,номер бита- инвертировать бит по адресу
wr|b адрес, номер бита - записать по логическому "И"
wr&b адрес, номер бита - записать по логическому "ИЛИ"
wr& адрес, данные - записать по логическому "ИЛИ"
wr& адрес, данные - записать по логическому "И"
wr1 начальный адрес, длина блока - заполнить единицами блок адресов
wr0 начальный адрес, длина блока - заполнить нолями блок адресов
rda начальный адрес, длина - считать массив байтов
boud9600 переключится через 3 секунды на скорость 9600
boud19200 переключится через 3 секунды на скорость 9600, если в течении 5 секунд не придет команда ok переключиться обратно на 9600.
Проверил не всё, если кто заметит ошибки, пишите, может исправлю ))
Если кому не лень исправте справку на русский, я забыл.
команды:
? - вывести список комманд
?? вывести названия регистров ATMEGA328 и адреса
boot- быстро мигнуть 3 раза светодиодом и через 3 секунды перейти в бутлоадер
boot5 - быстро мигнуть 5 раз и через 5 секунд перейти в бутлоадер
rd адрес - считать ячейку памяти
rdb адрес, номер бита - считать бит по адресу
erd адрес - считать ячейку EEPROM
erdb адрес, номер бита - считать бит из EEPROM по адресу
wr адрес данные - записать в ячейку данные
ewr адрес данные - записать в eeprom
clre - очистить EEPROM
wrsb адрес,номер бита- установить бит по адресу
wrrb адрес,номер бита- сбросить бит по адресу
wrib адрес,номер бита- инвертировать бит по адресу
wr|b адрес, номер бита - записать по логическому "И"
wr&b адрес, номер бита - записать по логическому "ИЛИ"
wr& адрес, данные - записать по логическому "ИЛИ"
wr& адрес, данные - записать по логическому "И"
wr1 начальный адрес, длина блока - заполнить единицами блок адресов
wr0 начальный адрес, длина блока - заполнить нолями блок адресов
rda начальный адрес, длина - считать массив байтов
boud9600 переключится через 3 секунды на скорость 9600
boud19200 переключится через 3 секунды на скорость 9600, если в течении 5 секунд не придет команда ok переключиться обратно на 9600.
Проверил не всё, если кто заметит ошибки, пишите, может исправлю ))
Если кому не лень исправте справку на русский, я забыл.
C++:
#include <EEPROM.h>
#include <avr/boot.h>
#include <avr/pgmspace.h>
// -------------------------------------------------------------------
// 1. КОНСТАНТЫ ИСПОЛЬЗУЮЩИЕ PROGMEM (Flash Memory)
// -------------------------------------------------------------------
// Макросы для работы с памятью и регистрами
#define READ_RAM_BYTE(addr) (*((volatile uint8_t *)(addr)))
#define WRITE_RAM_BYTE(addr, data) (*((volatile uint8_t *)(addr)) = (data))
const word BOOTLOADER_START = 0x3C00;
// --- Тексты помощи и сообщений ---
const char HELP_TEXT[] PROGMEM =
"--- Command List ---\n"
"?: Show command list\n"
"??: Show ATmega328 register names and addresses\n"
"boot <blinks> <delay_s>: Enter bootloader using default address (e.g., boot 3 3)\n"
"bootaddr <addr> <blinks> <delay_s>: Enter bootloader using custom address (e.g., bootaddr 3c00 5 5)\n"
"rd <addr>: Read RAM byte at address (hex)\n"
"rdb <addr>,<bit>: Read bit from RAM at address\n"
"wr <addr> <data>: Write byte to RAM (hex)\n"
"clre: Clear EEPROM (fill with 0xFF)\n"
"erd <addr>: Read EEPROM byte at address (hex)\n"
"erdb <addr>,<bit>: Read bit from EEPROM at address\n"
"ewr <addr> <data>: Write byte to EEPROM (hex)\n"
"wrsb <addr>,<bit>: Set bit (1) in RAM\n"
"wrrb <addr>,<bit>: Clear bit (0) in RAM\n"
"wrib <addr>,<bit>: Invert bit in RAM\n"
"wr|b <addr>,<bit>: Bitwise OR with 1 (same as wrsb)\n"
"wr&b <addr>,<bit>: Bitwise AND with 0 (same as wrrb)\n"
"wr| <addr> <data>: Bitwise OR with data in RAM\n"
"wr& <addr> <data>: Bitwise AND with data in RAM\n"
"wr1 <start_addr> <len>: Fill RAM block with 0xFF\n"
"wr0 <start_addr> <len>: Fill RAM block with 0x00\n"
"rda <start_addr> <len>: Read array of bytes from RAM\n"
"boud9600: Switch to 9600 baud in 3s\n"
"boud19200: Switch to 19200 baud in 3s (with 5s rollback)\n"
"--------------------";
const char REGISTER_HEADER[] PROGMEM = "--- ATmega328P Registers (partial list) ---";
const char REGISTER_FOOTER[] PROGMEM = "------------------------------------------";
// --- Сообщения об ошибках и статусе ---
const char ERR_RAM_BOUNDS[] PROGMEM = "Error: Address out of RAM bounds.";
const char ERR_BIT_INVALID[] PROGMEM = "Error: Invalid address or bit number (0-7).";
const char ERR_ADDR_LENGTH[] PROGMEM = "Error: Invalid start address or length.";
const char ERR_EEPROM_BOUNDS[] PROGMEM = "Error: Address out of EEPROM bounds.";
const char ERR_INTERNAL_OP[] PROGMEM = "Internal Error: Invalid operation.";
const char ERR_ADDR_FORMAT[] PROGMEM = "Error: Invalid bootloader address format or parameters.";
// --- Структура и список регистров (также в PROGMEM) ---
struct RegisterInfo {
const char* name;
uint16_t address;
};
// Частичный список регистров ATmega328P
const RegisterInfo registers[] PROGMEM = {
{"SREG", 0x5F}, // Status Register
{"SPH", 0x5E}, // Stack Pointer High
{"SPL", 0x5D}, // Stack Pointer Low
{"PORTB", 0x25}, // Port B Data Register
{"DDRB", 0x24}, // Port B Data Direction Register
{"PINB", 0x23}, // Port B Input Pins Address
{"UBRR0H", 0xC5},// UART Baud Rate Register High
{"UBRR0L", 0xC4},// UART Baud Rate Register Low
{"UCSR0A", 0xC0},// UART Control and Status Register A
// ... можно добавить остальные регистры
};
const int NUM_REGISTERS = sizeof(registers) / sizeof(registers[0]);
// -------------------------------------------------------------------
// 2. ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ PROGMEM
// -------------------------------------------------------------------
/**
* @brief Вывод строки, хранящейся в PROGMEM, в Serial.
*/
void printPgmString(const char* str) {
size_t len = strlen_P(str);
for (size_t i = 0; i < len; i++) {
Serial.write(pgm_read_byte(str + i));
}
}
/**
* @brief Вывод данных регистра (имя из PROGMEM)
*/
void printPgmRegisterInfo(int index) {
RegisterInfo info;
// Читаем структуру из PROGMEM в RAM
memcpy_P(&info, ®isters[index], sizeof(RegisterInfo));
Serial.print(info.name);
Serial.print(F(" ("));
Serial.print(index, DEC);
Serial.print(F("): 0x"));
if (info.address < 0x10) Serial.print('0');
Serial.println(info.address, HEX);
}
// -------------------------------------------------------------------
// 3. ОСНОВНОЙ КОД и ОБРАБОТЧИКИ
// -------------------------------------------------------------------
long currentBaudRate = 9600;
void setup() {
Serial.begin(currentBaudRate);
pinMode(LED_BUILTIN, OUTPUT);
Serial.println(F("UART Shell Ready. Type '?' for help."));
}
void loop() {
if (Serial.available()) {
String input = Serial.readStringUntil('\n');
input.trim();
if (input.length() > 0) {
parseCommand(input);
}
}
}
void printHelp() {
printPgmString(HELP_TEXT);
Serial.println();
}
void printRegisters() {
printPgmString(REGISTER_HEADER);
Serial.println();
for (int i = 0; i < NUM_REGISTERS; i++) {
printPgmRegisterInfo(i);
}
printPgmString(REGISTER_FOOTER);
Serial.println();
}
/**
* @brief Переход в бутлоадер
* @param boot_addr Адрес для перехода (0 - использовать BOOTLOADER_START)
* @param count Количество миганий
* @param delay_s Задержка в секундах
*/
void enterBootloader(uint16_t boot_addr, int count, int delay_s) {
// Мигание
for (int i = 0; i < count; i++) {
digitalWrite(LED_BUILTIN, HIGH);
delay(100);
digitalWrite(LED_BUILTIN, LOW);
delay(100);
}
Serial.print(F("Entering bootloader at address 0x"));
if (boot_addr == 0) {
boot_addr = BOOTLOADER_START;
Serial.print(boot_addr, HEX);
Serial.print(F(" (Default) in "));
} else {
Serial.print(boot_addr, HEX);
Serial.print(F(" (Custom) in "));
}
Serial.print(delay_s);
Serial.println(F(" seconds..."));
delay(delay_s * 1000);
void (*bootloader_ptr)(void) = (void (*)(void))boot_addr;
cli();
bootloader_ptr();
}
void readRam(unsigned int addr) {
if (addr > RAMEND) {
printPgmString(ERR_RAM_BOUNDS);
Serial.println();
return;
}
uint8_t data = READ_RAM_BYTE(addr);
Serial.print(F("RAM[0x"));
Serial.print(addr, HEX);
Serial.print(F("] = 0x"));
Serial.println(data, HEX);
}
void readRamBit(unsigned int addr, int bitNum) {
if (addr > RAMEND || bitNum < 0 || bitNum > 7) {
printPgmString(ERR_BIT_INVALID);
Serial.println();
return;
}
uint8_t data = READ_RAM_BYTE(addr);
uint8_t bitValue = (data >> bitNum) & 0x01;
Serial.print(F("RAM[0x"));
Serial.print(addr, HEX);
Serial.print(F("].Bit"));
Serial.print(bitNum, DEC);
Serial.print(F(" = "));
Serial.println(bitValue, DEC);
}
void writeRam(unsigned int addr, uint8_t data) {
if (addr > RAMEND) {
printPgmString(ERR_RAM_BOUNDS);
Serial.println();
return;
}
WRITE_RAM_BYTE(addr, data);
Serial.print(F("Wrote 0x"));
Serial.print(data, HEX);
Serial.print(F(" to RAM[0x"));
Serial.print(addr, HEX);
Serial.println(F("]."));
}
void readEeprom(unsigned int addr) {
if (addr >= EEPROM.length()) {
printPgmString(ERR_EEPROM_BOUNDS);
Serial.println();
return;
}
uint8_t data = EEPROM.read(addr);
Serial.print(F("EEPROM[0x"));
Serial.print(addr, HEX);
Serial.print(F("] = 0x"));
Serial.println(data, HEX);
}
void readEepromBit(unsigned int addr, int bitNum) {
if (addr >= EEPROM.length() || bitNum < 0 || bitNum > 7) {
printPgmString(ERR_BIT_INVALID);
Serial.println();
return;
}
uint8_t data = EEPROM.read(addr);
uint8_t bitValue = (data >> bitNum) & 0x01;
Serial.print(F("EEPROM[0x"));
Serial.print(addr, HEX);
Serial.print(F("].Bit"));
Serial.print(bitNum, DEC);
Serial.print(F(" = "));
Serial.println(bitValue, DEC);
}
void writeEeprom(unsigned int addr, uint8_t data) {
if (addr >= EEPROM.length()) {
printPgmString(ERR_EEPROM_BOUNDS);
Serial.println();
return;
}
EEPROM.write(addr, data);
Serial.print(F("Wrote 0x"));
Serial.print(data, HEX);
Serial.print(F(" to EEPROM[0x"));
Serial.print(addr, HEX);
Serial.println(F("]."));
}
void clearEeprom() {
Serial.println(F("Clearing EEPROM... (May take a moment)"));
for (int i = 0; i < EEPROM.length(); i++) {
EEPROM.write(i, 0xFF);
}
Serial.println(F("EEPROM cleared to 0xFF."));
}
void manipulateRamBit(unsigned int addr, int bitNum, char operation) {
if (addr > RAMEND || bitNum < 0 || bitNum > 7) {
printPgmString(ERR_BIT_INVALID);
Serial.println();
return;
}
uint8_t data = READ_RAM_BYTE(addr);
uint8_t mask = 1 << bitNum;
const char* op_msg;
switch (operation) {
case 's': // wrsb
data |= mask;
op_msg = "Set bit (wrsb)";
break;
case 'r': // wrrb
data &= ~mask;
op_msg = "Clear bit (wrrb)";
break;
case 'i': // wrib
data ^= mask;
op_msg = "Invert bit (wrib)";
break;
case '|': // wr|b
data |= mask;
op_msg = "Bitwise OR with 1 (wr|b)";
break;
case '&': // wr&b
data &= ~mask;
op_msg = "Bitwise AND with 0 (wr&b)";
break;
default:
printPgmString(ERR_INTERNAL_OP);
Serial.println();
return;
}
WRITE_RAM_BYTE(addr, data);
Serial.print(op_msg);
Serial.print(F(" at RAM[0x"));
Serial.print(addr, HEX);
Serial.print(F("]. Result: 0x"));
Serial.println(data, HEX);
}
void binaryRamOp(unsigned int addr, uint8_t data, char operation) {
if (addr > RAMEND) {
printPgmString(ERR_RAM_BOUNDS);
Serial.println();
return;
}
uint8_t currentData = READ_RAM_BYTE(addr);
const char* op_msg;
switch (operation) {
case '|': // wr|
currentData |= data;
op_msg = "Bitwise OR (wr|)";
break;
case '&': // wr&
currentData &= data;
op_msg = "Bitwise AND (wr&)";
break;
default:
printPgmString(ERR_INTERNAL_OP);
Serial.println();
return;
}
WRITE_RAM_BYTE(addr, currentData);
Serial.print(op_msg);
Serial.print(F(" with 0x"));
Serial.print(data, HEX);
Serial.print(F(" at RAM[0x"));
Serial.print(addr, HEX);
Serial.print(F("]. New Value: 0x"));
Serial.println(currentData, HEX);
}
void fillRamBlock(unsigned int startAddr, unsigned int len, uint8_t fillByte) {
if (startAddr > RAMEND || startAddr + len > RAMEND || len == 0) {
printPgmString(ERR_ADDR_LENGTH);
Serial.println();
return;
}
for (unsigned int i = 0; i < len; i++) {
WRITE_RAM_BYTE(startAddr + i, fillByte);
}
Serial.print(F("Filled RAM block from 0x"));
Serial.print(startAddr, HEX);
Serial.print(F(" to 0x"));
Serial.print(startAddr + len - 1, HEX);
Serial.print(F(" with 0x"));
Serial.println(fillByte, HEX);
}
void readRamArray(unsigned int startAddr, unsigned int len) {
if (startAddr > RAMEND || startAddr + len > RAMEND || len == 0) {
printPgmString(ERR_ADDR_LENGTH);
Serial.println();
return;
}
Serial.print(F("RAM[0x"));
Serial.print(startAddr, HEX);
Serial.print(F("..0x"));
Serial.print(startAddr + len - 1, HEX);
Serial.println(F("]:"));
for (unsigned int i = 0; i < len; i++) {
uint8_t data = READ_RAM_BYTE(startAddr + i);
if (i > 0 && i % 16 == 0) {
Serial.println();
Serial.print(F("0x"));
Serial.print(startAddr + i, HEX);
Serial.print(F(": "));
} else if (i % 16 == 0) {
Serial.print(F("0x"));
Serial.print(startAddr + i, HEX);
Serial.print(F(": "));
}
if (data < 0x10) Serial.print('0');
Serial.print(data, HEX);
Serial.print(F(" "));
}
Serial.println();
}
void changeBaudRateWithRollback(long newBaudRate, bool rollback) {
Serial.print(F("Switching to "));
Serial.print(newBaudRate);
Serial.println(F(" baud in 3 seconds. Send 'ok' to confirm."));
delay(3000);
Serial.end();
Serial.begin(newBaudRate);
currentBaudRate = newBaudRate;
Serial.print(F("Switched to "));
Serial.print(currentBaudRate);
Serial.println(F(" baud. (If you don't see this, reconnect with the new speed)"));
if (rollback) {
long startTime = millis();
bool confirmed = false;
// Блокирующий цикл ожидания подтверждения
while (millis() - startTime < 5000) {
if (Serial.available()) {
String confirm = Serial.readStringUntil('\n');
confirm.trim();
if (confirm.equalsIgnoreCase("ok")) {
confirmed = true;
Serial.println(F("Confirmed. Baud rate change permanent."));
break;
}
}
}
if (!confirmed) {
Serial.println(F("Timeout. Rolling back to 9600 baud..."));
delay(100);
Serial.end();
Serial.begin(9600);
currentBaudRate = 9600;
Serial.println(F("Rolled back to 9600 baud."));
}
}
}
void parseCommand(String input) {
// Разделение строки на слова
char command[input.length() + 1];
input.toCharArray(command, input.length() + 1);
char *p = command;
char *str;
char *words[5] = {NULL}; // Максимум 5 слов: <cmd> <p1> <p2> <p3> <p4>
int wordCount = 0;
while ((str = strtok_r(p, " ,", &p))) {
if (wordCount < 5) {
words[wordCount++] = str;
} else {
break;
}
}
if (wordCount == 0) return;
// --- Параметры для команды boot/bootaddr ---
uint16_t boot_addr = 0; // 0 - использовать адрес по умолчанию
int blinks = 0;
int delay_s = 0;
// --- Параметры для команд памяти ---
unsigned int addr = (wordCount > 1) ? (unsigned int)strtol(words[1], NULL, 16) : 0;
int bitNum = 0;
uint8_t data = 0;
unsigned int len = 0;
if (wordCount > 2) {
// Определяем второй аргумент в зависимости от команды
if (strcmp(words[0], "rdb") == 0 || strcmp(words[0], "erdb") == 0 ||
strcmp(words[0], "wrsb") == 0 || strcmp(words[0], "wrrb") == 0 ||
strcmp(words[0], "wrib") == 0 || strcmp(words[0], "wr|b") == 0 || strcmp(words[0], "wr&b") == 0) {
bitNum = (int)strtol(words[2], NULL, 10);
} else if (strcmp(words[0], "wr") == 0 || strcmp(words[0], "ewr") == 0 ||
strcmp(words[0], "wr|") == 0 || strcmp(words[0], "wr&") == 0) {
data = (uint8_t)strtol(words[2], NULL, 16);
} else if (strcmp(words[0], "wr1") == 0 || strcmp(words[0], "wr0") == 0 ||
strcmp(words[0], "rda") == 0) {
len = (unsigned int)strtol(words[2], NULL, 10);
}
}
// --- Обработка команд ---
if (strcmp(words[0], "?") == 0) {
printHelp();
} else if (strcmp(words[0], "??") == 0) {
printRegisters();
} else if (strcmp(words[0], "boot") == 0) {
// boot <blinks> <delay_s>
if (wordCount == 3) {
blinks = (int)strtol(words[1], NULL, 10);
delay_s = (int)strtol(words[2], NULL, 10);
if (blinks > 0 && delay_s > 0) {
enterBootloader(0, blinks, delay_s); // 0 = адрес по умолчанию
} else {
Serial.println(F("Error: Blinks and delay must be positive."));
}
} else {
Serial.println(F("Error: Usage is 'boot <blinks> <delay_s>'."));
}
} else if (strcmp(words[0], "boot5") == 0) {
// Старая команда boot5, теперь просто вызывается boot 5 5
enterBootloader(0, 5, 5);
} else if (strcmp(words[0], "bootaddr") == 0) {
// bootaddr <addr> <blinks> <delay_s>
if (wordCount == 4) {
boot_addr = (uint16_t)strtol(words[1], NULL, 16);
blinks = (int)strtol(words[2], NULL, 10);
delay_s = (int)strtol(words[3], NULL, 10);
if (boot_addr != 0 && blinks > 0 && delay_s > 0) {
enterBootloader(boot_addr, blinks, delay_s);
} else {
printPgmString(ERR_ADDR_FORMAT);
Serial.println();
}
} else {
Serial.println(F("Error: Usage is 'bootaddr <addr> <blinks> <delay_s>'."));
}
} else if (strcmp(words[0], "rd") == 0 && wordCount == 2) {
readRam(addr);
} else if (strcmp(words[0], "rdb") == 0 && wordCount == 3) {
readRamBit(addr, bitNum);
} else if (strcmp(words[0], "erd") == 0 && wordCount == 2) {
readEeprom(addr);
} else if (strcmp(words[0], "erdb") == 0 && wordCount == 3) {
readEepromBit(addr, bitNum);
} else if (strcmp(words[0], "wr") == 0 && wordCount == 3) {
writeRam(addr, data);
} else if (strcmp(words[0], "ewr") == 0 && wordCount == 3) {
writeEeprom(addr, data);
} else if (strcmp(words[0], "clre") == 0) {
clearEeprom();
} else if (strcmp(words[0], "wrsb") == 0 && wordCount == 3) {
manipulateRamBit(addr, bitNum, 's');
} else if (strcmp(words[0], "wrrb") == 0 && wordCount == 3) {
manipulateRamBit(addr, bitNum, 'r');
} else if (strcmp(words[0], "wrib") == 0 && wordCount == 3) {
manipulateRamBit(addr, bitNum, 'i');
} else if (strcmp(words[0], "wr|b") == 0 && wordCount == 3) {
manipulateRamBit(addr, bitNum, '|');
} else if (strcmp(words[0], "wr&b") == 0 && wordCount == 3) {
manipulateRamBit(addr, bitNum, '&');
} else if (strcmp(words[0], "wr|") == 0 && wordCount == 3) {
binaryRamOp(addr, data, '|');
} else if (strcmp(words[0], "wr&") == 0 && wordCount == 3) {
binaryRamOp(addr, data, '&');
} else if (strcmp(words[0], "wr1") == 0 && wordCount == 3) {
fillRamBlock(addr, len, 0xFF);
} else if (strcmp(words[0], "wr0") == 0 && wordCount == 3) {
fillRamBlock(addr, len, 0x00);
} else if (strcmp(words[0], "rda") == 0 && wordCount == 3) {
readRamArray(addr, len);
} else if (strcmp(words[0], "boud9600") == 0) {
changeBaudRateWithRollback(9600, false);
} else if (strcmp(words[0], "boud19200") == 0) {
changeBaudRateWithRollback(19200, true);
} else {
Serial.print(F("Unknown command: "));
Serial.println(input);
Serial.println(F("Type '?' for help."));
}
}