Convert a string to a pre-defined register

Hi everyone,

I used Serial.println(TCCR1A, HEX) to print the value of TCCR1A on the serial port screen, and this worked perfectly fine.

However, I want to type a register name into the serial port and print its value. Here is my little code:

void loop()
{
while(Serial.available()==0){} // wait for data input
Serial.print("Enter data: ");
String inputString = Serial.readString(); // read string
inputString.trim(); // remove any \r \n whitespace at the end of the String
Serial.println(XXXXXXX, HEX);
}

How to convert the string "inputString" to a pre-defined register "TCCR1A", so the Serial.println() can print out the value of TCCR1A?

Thank you so much.

Mtuan

You can't do it that way because you are using char or string, the TCCR1A is a defined var. Find out what is under the covers so to speak for TCCR1A and then maybe you can enter it but I bet you need to convert it to byte or int.

There is no place in the build environment where register names like "TCCR1A" are defined as strings. So effectively, there is no way to do this without a LOT of work for build a string database.

In theory, Microchip has nice .atdf files (distributed with their "packs") that describe each chip in nice, parseable XML files, that you could process with Python and an XML library and produce such string tables. I've partially done that here: GitHub - WestfW/Atmel_ATDF_parser: Python code that parses the XML from an Atmel .ATDF Device Description file, and allows you to find/print various information and relationships that are not present in the .h files.

Or you can go through .h files with some heavy duty editor macros or code, and produce something similar.

I think both with used to produce this SAMD Explorer sketch.

But this is unlikely to be a "beginner" solution.

TCCR1A, as well as many of the other register names, are defined in the following file:

.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/avr/include/avr/iom328p.h

Once you know the names, it should just be a matter of checking to see if the string from the serial input matches the name, then executing the print statement.

void setup() {
  Serial.begin(115200);
}
void loop()
{
  while (Serial.available() == 0) {} // wait for data input
  Serial.print("Enter data: ");
  String inputString = Serial.readString(); // read string
  inputString.trim(); // remove any \r \n whitespace at the end of the String
  if (inputString == "TCCR1A") {
    Serial.println(TCCR1A, HEX);
  }
  if (inputString == "TCCR1B") {
    Serial.println(TCCR1B, HEX);
  }
}

What we put in source code gets used by the compiler which is done when it makes the hex file loaded into the chip on board.

Bu-uh-uh-uut -- this looks like what I recall:

Memory Map Breakdown:
General Purpose Registers:
.
R0-R31 are the first 32 bytes in the data memory. 
I/O Registers:
.
These occupy the next 64 bytes (addresses 0x20 to 0x5F) and control the microcontroller's pins and peripherals. 
Extended I/O:
.
Some AVRs have extended I/O memory, which is part of the SRAM and handles additional peripherals. 
SRAM:
.
This is used for temporary data storage, stack, and other application-specific purposes. 
Key Concepts:
I/O Registers:
.
These registers control the microcontroller's I/O ports, timers, interrupts, and other peripheral functions. 
Memory-Mapped I/O:
.
I/O registers are memory-mapped, meaning they are accessible using the same instructions that access data memory. 
Special Instructions:
.
The AVR has instructions like IN, OUT, SBI, and CBI that are specifically designed for working with I/O registers. 
In Summary:
The AVR memory map is divided into sections for general purpose registers, I/O registers, and SRAM. I/O registers are accessed using memory-mapped I/O and have specific instructions for manipulating them. 

You can map the names to addresses and access registers that way. For printing purposes, maybe a hex dump in nice rows and columns would suit?

And if want to save the selection for later use (to change register's contents, etc):

void setup() {
  Serial.begin(115200);
  delay(1000);
}

void loop() {
  using RegAddress = decltype(&TCCR1A);
  RegAddress selectedReg {nullptr};

  while (Serial.available() == 0) {
  }
  Serial.print("Enter data: ");
  String inputString {Serial.readString()};
  inputString.trim();

  if (inputString == "TCCR1A") {
    selectedReg = &TCCR1A;
  } else if (inputString == "TCCR1B") {
    selectedReg = &TCCR1B;
  } else if (inputString == "TCCR1C") {
    selectedReg = &TCCR1C;
  }

  if (selectedReg != nullptr) {
    // Do something here with selected register
    // For now we'll just print the contents
    Serial.print("Register ");
    Serial.print(inputString);
    Serial.print(": ");
    Serial.println(*selectedReg, HEX);
  }
}

The python program I linked earlier is actually closer to immediately useful than I thought. Here's a nice table for you. (naturally, you can edit it down to only the registers you're interested in. And for that matter, it wouldn't be hard to set up a partial table without the python code (which is mainly useful for extracting ALL the register names.)

#include <avr/io.h>
#include <stdio.h>

struct { char name[7];
  void * address;
} atmega328_regs[] = {
  {"UDR0",  (void *) &UDR0 },
  {"UCSR0A",  (void *) &UCSR0A },
  {"UCSR0B",  (void *) &UCSR0B },
  {"UCSR0C",  (void *) &UCSR0C },
  {"UBRR0",  (void *) &UBRR0 },
  {"TWAMR",  (void *) &TWAMR },
  {"TWBR",  (void *) &TWBR },
  {"TWCR",  (void *) &TWCR },
  {"TWSR",  (void *) &TWSR },
  {"TWDR",  (void *) &TWDR },
  {"TWAR",  (void *) &TWAR },
  {"TIMSK1",  (void *) &TIMSK1 },
  {"TIFR1",  (void *) &TIFR1 },
  {"TCCR1A",  (void *) &TCCR1A },
  {"TCCR1B",  (void *) &TCCR1B },
  {"TCCR1C",  (void *) &TCCR1C },
  {"TCNT1",  (void *) &TCNT1 },
  {"OCR1A",  (void *) &OCR1A },
  {"OCR1B",  (void *) &OCR1B },
  {"ICR1",  (void *) &ICR1 },
  {"GTCCR",  (void *) &GTCCR },
  {"TIMSK2",  (void *) &TIMSK2 },
  {"TIFR2",  (void *) &TIFR2 },
  {"TCCR2A",  (void *) &TCCR2A },
  {"TCCR2B",  (void *) &TCCR2B },
  {"TCNT2",  (void *) &TCNT2 },
  {"OCR2B",  (void *) &OCR2B },
  {"OCR2A",  (void *) &OCR2A },
  {"ASSR",  (void *) &ASSR },
  {"GTCCR",  (void *) &GTCCR },
  {"ADMUX",  (void *) &ADMUX },
  {"ADC",  (void *) &ADC },
  {"ADCSRA",  (void *) &ADCSRA },
  {"ADCSRB",  (void *) &ADCSRB },
  {"DIDR0",  (void *) &DIDR0 },
  {"ACSR",  (void *) &ACSR },
  {"DIDR1",  (void *) &DIDR1 },
  {"PORTB",  (void *) &PORTB },
  {"DDRB",  (void *) &DDRB },
  {"PINB",  (void *) &PINB },
  {"PORTC",  (void *) &PORTC },
  {"DDRC",  (void *) &DDRC },
  {"PINC",  (void *) &PINC },
  {"PORTD",  (void *) &PORTD },
  {"DDRD",  (void *) &DDRD },
  {"PIND",  (void *) &PIND },
  {"OCR0B",  (void *) &OCR0B },
  {"OCR0A",  (void *) &OCR0A },
  {"TCNT0",  (void *) &TCNT0 },
  {"TCCR0B",  (void *) &TCCR0B },
  {"TCCR0A",  (void *) &TCCR0A },
  {"TIMSK0",  (void *) &TIMSK0 },
  {"TIFR0",  (void *) &TIFR0 },
  {"GTCCR",  (void *) &GTCCR },
  {"EICRA",  (void *) &EICRA },
  {"EIMSK",  (void *) &EIMSK },
  {"EIFR",  (void *) &EIFR },
  {"PCICR",  (void *) &PCICR },
  {"PCMSK2",  (void *) &PCMSK2 },
  {"PCMSK1",  (void *) &PCMSK1 },
  {"PCMSK0",  (void *) &PCMSK0 },
  {"PCIFR",  (void *) &PCIFR },
  {"SPDR",  (void *) &SPDR },
  {"SPSR",  (void *) &SPSR },
  {"SPCR",  (void *) &SPCR },
  {"WDTCSR",  (void *) &WDTCSR },
  {"PRR",  (void *) &PRR },
  {"OSCCAL",  (void *) &OSCCAL },
  {"CLKPR",  (void *) &CLKPR },
  {"SREG",  (void *) &SREG },
  {"SP",  (void *) &SP },
  {"SPMCSR",  (void *) &SPMCSR },
  {"MCUCR",  (void *) &MCUCR },
  {"MCUSR",  (void *) &MCUSR },
  {"SMCR",  (void *) &SMCR },
  {"GPIOR2",  (void *) &GPIOR2 },
  {"GPIOR1",  (void *) &GPIOR1 },
  {"GPIOR0",  (void *) &GPIOR0 },
  {"EEAR",  (void *) &EEAR },
  {"EEDR",  (void *) &EEDR },
  {"EECR",  (void *) &EECR },
};

#define NREGS (sizeof(atmega328_regs)/sizeof(atmega328_regs[0]))

void setup() {
  Serial.begin(115200);
  for (int i=0; i < NREGS; i++) {
    Serial.print(atmega328_regs[i].name);
    Serial.print(" is at 0x");
    Serial.println((int)atmega328_regs[i].address, HEX);
  }
}

void loop() {}

Looking up strings in the table, possibly moving it into program memory, and possibly adjusting the address value to a proper pointer, is left as an exercise for the student...


void loop()
{
    if (Serial.available())  {
        char buf [90];
        int  n = Serial.readBytesUntil ('\n', buf, sizeof(buf)-1);
        buf [n] = '\0';

        if (! strcmp (buf, "TCCR1A"))
            Serial.println (TCCR1A);
    }
}

void setup ()
{
    Serial.begin (9600);
}

Thanks for all of your great advice. Here is my little code to check TCCR1A and TCCR1B.

void loop()
{
while(Serial.available()==0){} // wait for data available
Serial.print("Enter data: ");
inputString = Serial.readString(); // read string
inputString.trim(); // remove any \r \n whitespace at the end of the String
if (inputString=="TCCR1A"){
Serial.println(TCCR1A, HEX);}
else if(inputString=="TCCR1B"){
Serial.println(TCCR1B, HEX);}
else{
Serial.println("Check input");}
}