Introduction:
Registers in microcontrollers are small, fast-access memory locations integrated directly into the CPU. They serve as temporary storage for data and instructions during program execution. Understanding how to write to and read from registers in microcontrollers is crucial for effective embedded systems development. Registers serve as the interface between software and hardware components, it helps us to configure settings and control various aspects of a microcontroller's operation. Understanding register-level programming helps in debugging and troubleshooting. In this experiment, we are going to initialize registers, read and write them
Scope:
This project aims to develop a C program that allows users to select between reading the register value and writing data to it. It utilizes serial console software on a PC to establish communication with the microcontroller. The program facilitates adding new values to designated registers and reading existing register values via a serial terminal interface. Through this experiment, we will understand the working of registers, their functionalities, and the process of writing to and reading from them
Prerequisite:
3.1. Hardware:
Pico microcontroller
USB cable (to connect the computer)
3 .2. Software:
Thonny Software(For micro python)
MicroPython firmware installed on Raspberry Pi Pico
ubuntu Terminal(For embedded C)
CMake,Pico SDK,GNU Arm Embedded Toolchain,WSL.
Connection Details:

Working Principle:
Registers in microcontrollers are fundamental components embedded directly within the CPU, serving as temporary storage locations for data and instructions during program execution. They come in various types, including general-purpose registers, status registers, control registers, and special function registers (SFRs), each with its unique purpose and functionality. General-purpose registers are utilized for storing operands and intermediate results of arithmetic and logical operations, while status registers contain flags indicating operation outcomes like zero, carry, and overflow. Control registers manage CPU behavior and peripheral devices, such as clock speed, interrupt settings, and power management. Special function registers facilitate communication with peripherals, controlling their operation and configuration.
Registers act as the interface between software and hardware components, allowing us to configure settings and control different aspects of a microcontroller's operation.
In the C code, an array of Register structures is initialized to represent 10 registers. Each register is assigned a base address starting from 0x50000000 and incremented by 4 bytes. The program provides two main functions: reading data from a register and writing data to a register.
In the read_data_from_reg function, it checks if the provided register address matches any of the predefined register addresses. If found, it prints the data stored in that register. Similarly, in the write_data_to_reg function, it checks if the provided register address is valid and then writes the provided data to that register.
The main function presents a menu to the user with options to read from a register, write to a register, or exit the program. It prompts the user for their choice and based on the selection, it either reads data from a register, writes data to a register, or terminates the program if chosen. The program continues to loop until the user selects the exit option.
Application code:
MicroPython:
import machine
import ustruct
import sys
def read_register(addr):
try:
reg = machine.mem32[addr]
return reg
except Exception as e:
print(f"Error reading from address {hex(addr)}: {str(e)}")
return None
def write_register(addr, value):
try:
machine.mem32[addr] = value
return True
except Exception as e:
print(f"Error writing to address {hex(addr)}: {str(e)}")
return False
# Main function to handle user input
while True:
print("Enter '1' to read register value.")
print("Enter '2' to write register value.")
print("Enter 'q' to quit.")
choice = input("Enter your choice: ").strip()
if choice == '1':
try:
addr = int(input("Enter register address (in hex, e.g., 0x40000000): ").strip(), 16)
value = read_register(addr)
if value is not None:
print(f"Value at address {hex(addr)}: {hex(value)}")
else:
print(f"Failed to read from address {hex(addr)}")
except ValueError:
print("Invalid address format. Please enter a valid hexadecimal address.")
elif choice == '2':
try:
addr = int(input("Enter register address (in hex, e.g., 0x40000000): ").strip(), 16)
value = int(input("Enter value to write (in hex, e.g., 0x1): ").strip(), 16)
if write_register(addr, value):
print(f"Written {hex(value)} to address {hex(addr)}")
else:
print(f"Failed to write to address {hex(addr)}")
except ValueError:
print("Invalid address or value format. Please enter valid hexadecimal numbers.")
elif choice == 'q':
print("Exiting...")
sys.exit()
else:
print("Invalid choice. Please enter '1', '2', or 'q'.")
Embedded C:
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
// Function to read a 32-bit register value
uint32_t read_register(uint32_t addr) {
volatile uint32_t *reg = (volatile uint32_t *)addr;
return *reg;
}
// Function to write a 32-bit value to a register
int write_register(uint32_t addr, uint32_t value) {
volatile uint32_t *reg = (volatile uint32_t *)addr;
*reg = value;
return 0;
}
int main() {
char choice;
uint32_t addr, value;
while (1) {
printf("Enter '1' to read register value.\n");
printf("Enter '2' to write register value.\n");
printf("Enter 'q' to quit.\n");
printf("Enter your choice: ");
choice = getchar();
getchar(); // Consume the newline character
if (choice == '1') {
printf("Enter register address (in hex, e.g., 0x40000000): ");
scanf("%x", &addr);
value = read_register(addr);
printf("Value at address 0x%08X: 0x%08X\n", addr, value);
} else if (choice == '2') {
printf("Enter register address (in hex, e.g., 0x40000000): ");
scanf("%x", &addr);
printf("Enter value to write (in hex, e.g., 0x1): ");
scanf("%x", &value);
if (write_register(addr, value) == 0) {
printf("Written 0x%08X to address 0x%08X\n", value, addr);
} else {
printf("Failed to write to address 0x%08X\n", addr);
}
} else if (choice == 'q') {
printf("Exiting...\n");
break;
} else {
printf("Invalid choice. Please enter '1', '2', or 'q'.\n");
}
// Clear input buffer
while (getchar() != '\n');
}
return 0;
}
Hardware Image:

Output:

Appendix:
Common CMakeLists:
cmake_minimum_required(VERSION 3.12)
# Pull in SDK (must be before project)
include(pico_sdk_import.cmake)
project(pico_experiments C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
if (PICO_SDK_VERSION_STRING VERSION_LESS "1.3.0")
message(FATAL_ERROR "Raspberry Pi Pico SDK version 1.3.0 (or later) required. Your version is ${PICO_SDK_VERSION_STRING}")
endif()
# Initialize the SDK
pico_sdk_init()
add_compile_options(-Wall
-Wno-format # int != int32_t as far as the compiler is concerned because gcc has int32_t as long int
-Wno-unused-function # we have some for the docs that arent called
)
if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
add_compile_options(-Wno-maybe-uninitialized)
endif()
# Hardware-specific examples in subdirectories:
add_subdirectory(expt_10_LCD)
CMakeLists:
cmake_minimum_required(VERSION 3.13)
include(pico_sdk_import.cmake)
project(RegisterRW C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
pico_sdk_init()
add_executable(RegisterRW main.c)
# Pull in pico_stdlib which provides common options
target_link_libraries(RegisterRW pico_stdlib)
# Enable USB output, disable UART output
pico_enable_stdio_usb(RegisterRW 1)
pico_enable_stdio_uart(RegisterRW 0)
# Create map/bin/hex file etc.
pico_add_extra_outputs(RegisterRW)
References:
These references provide comprehensive information and resources to help you understand, develop, and troubleshoot embedded C programs on the Raspberry Pi Pico platform. They cover everything from hardware specifications to software development tools and community support channels.
| # | Topic | Date & time | Action |
|---|
0% Completed (0/1)