Learning to Write Professional Software Code Style

Ya’akov

Joined Jan 27, 2019
10,235
I’m posting this code to test whether UART transmission and reception are working correctly on my ATmega8A using a USB–TTL converter. The code initializes UART at 9600 baud, and then continuously echoes back any character received. This helps confirm that both TX (sending) and RX (receiving) are functioning as expected.

C:
/*

    UCSRB = RX_ENABLE | TX_ENABLE;                    // Enable RX and TX
Just a point:
Comments are for why not what. The only reason for comments is if the information isn't in the code the comment refers to. The quoted line is just an example of a clear case.
 

Thread Starter

Embededd

Joined Jun 4, 2025
153
Comments are for why not what. The only reason for comments is if the information isn't in the code the comment refers to
I’ve gone through the code and reworked the comments so that they focus on why instead of just repeating what the code already say. I think I’m following your advice better now, but can you @Ya’akov check and let me know if I’m really doing it right?

C:
/*
* UART_Echo_Test.c
* Purpose:
*   Verify UART functionality on ATmega8A using echo test.
*   (If a character is typed on terminal, same character is echoed back.)
*
* Hardware:
*   - MCU: ATmega8A
*   - Clock: 12 MHz external crystal
*   - UART Pins: TXD (PD1), RXD (PD0)
*   - USB–TTL converter connected to PC terminal
*
* Toolchain:
*   - Language: C
*   - Compiler: AVR-GCC (via Microchip Studio)
*
* Author : Developer
* Created: 29-09-2025
*/

#define F_CPU       12000000UL  
#define BAUD        9600        
#define UBRR_VAL    ((F_CPU / (16UL * BAUD)) - 1)   // Datasheet formula ensures accurate baud

#include <avr/io.h>
#include <util/delay.h>

/* Bit masks to make register operations self-documenting */
#define RX_COMPLETE  (1 << RXC)
#define TX_READY     (1 << UDRE)
#define RX_ENABLE    (1 << RXEN)
#define TX_ENABLE    (1 << TXEN)
#define UCSRC_SELECT (1 << URSEL)
#define UCSZ_8BIT    ((1 << UCSZ0) | (1 << UCSZ1))

/**
* UART_Init
* Sets baud rate and frame format so that the MCU’s UART
* matches common PC terminal settings (9600 baud, 8N1).
*/
void UART_Init(void) {
    UBRRH = (unsigned char)(UBRR_VAL >> 8);
    UBRRL = (unsigned char)(UBRR_VAL & 0xFF);

    UCSRB = RX_ENABLE | TX_ENABLE;    // enable RX/TX so data can flow both ways
    UCSRC = UCSRC_SELECT | UCSZ_8BIT; // 8-bit frame matches standard terminal setup
}

/**
* UART_RxChar
* Blocks until data arrives,
* always gets a valid received byte.
*/
unsigned char UART_RxChar(void) {
    while (!(UCSRA & RX_COMPLETE)) {
        // Busy-wait until RXC flag signals data is available
    }
    return UDR;
}

/**
* UART_TxChar
* Blocks until the transmit buffer is free to ensure
* characters are not lost or overwritten.
*/
void UART_TxChar(char ch) {
    while (!(UCSRA & TX_READY)) {
        // Busy-wait until UDRE flag signals buffer empty
    }
    UDR = ch;
}

/**
* UART_SendString
* send status/debug messages
*      without calling UART_TxChar repeatedly.
*/
void UART_SendString(const char *str) {
    while (*str) {
        UART_TxChar(*str++);
    }
}

int main(void) {
    UART_Init();
    _delay_ms(100);  // allow terminal/USB-TTL to stabilize

    // UART_SendString("UART Echo Test Ready\r\n");

    while (1) {
        unsigned char c = UART_RxChar();  // wait for input from user
        UART_TxChar(c);                   // echo back for verification
    }
}
 

Dave Lowther

Joined Sep 8, 2016
332
I’m honestly not sure where to start. it feels a bit overwhelming
I've been writing software for almost 50 years. I remember that when I was a beginner I didn't have the skills to move away from the code and think about the design. IIRC I had to write little parts of the code to get a better understanding of what code I needed to write. It took me some time to become comfortable with not thinking at the code level when starting a project. When I was a beginner I didn't have help from people more experience, perhaps that was my problem, or perhaps some people need to learn a different way initially.
 

Futurist

Joined Apr 8, 2025
748
I’ve gone through the code and reworked the comments so that they focus on why instead of just repeating what the code already say. I think I’m following your advice better now, but can you @Ya’akov check and let me know if I’m really doing it right?

C:
/*
* UART_Echo_Test.c
* Purpose:
*   Verify UART functionality on ATmega8A using echo test.
*   (If a character is typed on terminal, same character is echoed back.)
*
* Hardware:
*   - MCU: ATmega8A
*   - Clock: 12 MHz external crystal
*   - UART Pins: TXD (PD1), RXD (PD0)
*   - USB–TTL converter connected to PC terminal
*
* Toolchain:
*   - Language: C
*   - Compiler: AVR-GCC (via Microchip Studio)
*
* Author : Developer
* Created: 29-09-2025
*/

#define F_CPU       12000000UL
#define BAUD        9600 
#define UBRR_VAL    ((F_CPU / (16UL * BAUD)) - 1)   // Datasheet formula ensures accurate baud

#include <avr/io.h>
#include <util/delay.h>

/* Bit masks to make register operations self-documenting */
#define RX_COMPLETE  (1 << RXC)
#define TX_READY     (1 << UDRE)
#define RX_ENABLE    (1 << RXEN)
#define TX_ENABLE    (1 << TXEN)
#define UCSRC_SELECT (1 << URSEL)
#define UCSZ_8BIT    ((1 << UCSZ0) | (1 << UCSZ1))

/**
* UART_Init
* Sets baud rate and frame format so that the MCU’s UART
* matches common PC terminal settings (9600 baud, 8N1).
*/
void UART_Init(void) {
    UBRRH = (unsigned char)(UBRR_VAL >> 8);
    UBRRL = (unsigned char)(UBRR_VAL & 0xFF);

    UCSRB = RX_ENABLE | TX_ENABLE;    // enable RX/TX so data can flow both ways
    UCSRC = UCSRC_SELECT | UCSZ_8BIT; // 8-bit frame matches standard terminal setup
}

/**
* UART_RxChar
* Blocks until data arrives,
* always gets a valid received byte.
*/
unsigned char UART_RxChar(void) {
    while (!(UCSRA & RX_COMPLETE)) {
        // Busy-wait until RXC flag signals data is available
    }
    return UDR;
}

/**
* UART_TxChar
* Blocks until the transmit buffer is free to ensure
* characters are not lost or overwritten.
*/
void UART_TxChar(char ch) {
    while (!(UCSRA & TX_READY)) {
        // Busy-wait until UDRE flag signals buffer empty
    }
    UDR = ch;
}

/**
* UART_SendString
* send status/debug messages
*      without calling UART_TxChar repeatedly.
*/
void UART_SendString(const char *str) {
    while (*str) {
        UART_TxChar(*str++);
    }
}

int main(void) {
    UART_Init();
    _delay_ms(100);  // allow terminal/USB-TTL to stabilize

    // UART_SendString("UART Echo Test Ready\r\n");

    while (1) {
        unsigned char c = UART_RxChar();  // wait for input from user
        UART_TxChar(c);                   // echo back for verification
    }
}
Understand, there are no rules other than those adopted by people and groups. You might work at Motorola and they will have a set of rules for commenting, variable naming, macro naming, file naming. They might have rules for how args are to be passed and the order of those args. They might have rules for function names, order of function within files, or even the number of files and so on.

You might then go to Intel and find they use a very different set of rules and you need to adjust your working method to transition your code style.

I've seen this all my life over almost fifty years. I worked in banks and trading firms and these all had "coding standards" and they were the backbone of how they reviewed code.

Some standards insist on block comments at the start of every function and the comment must define every arg, the return data and an overview of what the function does and helpful additional details.

So asking about stuff like this is ultimately subjective, I wouldn't dwell on this too much other than perhaps adopting some basic C style guidelines to get you started.

Take a look at this standard, at least in interviews you can tell them you wanted to use some kind of standard so settled on this, they'll respect the fact you took the trouble to do something like this, in an interview I'd be impressed if an inexperienced candidate took the initiative to voluntarily adopt something like this because it demonstrates an awareness of teamwork.



1759329837510.png

Barr - Embedded C Coding Standard.

1759329750558.png
 
Last edited:

AutoTangle

Joined Jun 13, 2023
2
Here's a big thanks to the OP and all the good planning, style, & standards advice. I'm in the same boat - an electronics HW guy trying to raise the quality of my code for some work projects, without making all the mistakes myself and learning the hard way. Even though a mentor of mine was nicknamed "Hard Way".
 

AutoTangle

Joined Jun 13, 2023
2
And especially, thanks for the planning comments. I've had some projects truly suffer from lack of planning and foresight. Planning & visualization tools are sooo interesting to me.
 

Thread Starter

Embededd

Joined Jun 4, 2025
153
I've been writing software for almost 50 years. I remember that when I was a beginner I didn't have the skills to move away from the code and think about the design. IIRC I had to write little parts of the code to get a better understanding of what code I needed to write. It took me some time to become comfortable with not thinking at the code level when starting a project. When I was a beginner I didn't have help from people more experience, perhaps that was my problem, or perhaps some people need to learn a different way initially.
Yes, I completely agree with you as a beginner it’s difficult to think at the design level right away, and like you said, writing small pieces of code first really helps in building that understanding. I think you may have missed my other thread Design Process where I was trying to learn from requirements through to deployment. I couldn’t cover the whole journey here, but I did my best. Even though it’s been overwhelming at times, I haven’t given up. I’ve just adjusted my approach, but my target is still clear.

The members here are very supportive, and I’ve already learned a lot from them and I’m still learning every day
 

Thread Starter

Embededd

Joined Jun 4, 2025
153
I do have a plan in mind and I’m moving forward step by step. For example, I knew early on that I would need LCD, RTC, and UART support in project, so I decided to start by developing the drivers first. I’ve already got the UART and LCD drivers working, and now I’m working on the I2C driver. Once the driver level work is done, I’ll be able to move on to the application level.
 

Thread Starter

Embededd

Joined Jun 4, 2025
153
Here's a big thanks to the OP and all the good planning, style, & standards advice. I'm in the same boat - an electronics HW guy trying to raise the quality of my code for some work projects, without making all the mistakes myself and learning the hard way. Even though a mentor of mine was nicknamed "Hard Way
I’m really grateful for all the advice and guidance shared here. That’s why I keep posting my code and sharing my thoughts. I believe there’s no harm in taking a second opinion. With so many experienced people around, it feels like we’re never stuck alone and can always get pointed in the right direction. That support really helps me to keep improving skills
 

Futurist

Joined Apr 8, 2025
748
I do have a plan in mind and I’m moving forward step by step. For example, I knew early on that I would need LCD, RTC, and UART support in project, so I decided to start by developing the drivers first. I’ve already got the UART and LCD drivers working, and now I’m working on the I2C driver. Once the driver level work is done, I’ll be able to move on to the application level.
What governed your choice or platform - ATmega 8 MCU ?
 

Thread Starter

Embededd

Joined Jun 4, 2025
153
What governed your choice or platform - ATmega 8 MCU ?
I already had the ATmega8A hardware in hand, and since it’s simple and good for learning, I went ahead with it. Everything is wired up on a breadboard, so once I get a proof of concept working with this, I’d like to move on to using an ARM MCU
 

Futurist

Joined Apr 8, 2025
748
I already had the ATmega8A hardware in hand, and since it’s simple and good for learning, I went ahead with it. Everything is wired up on a breadboard, so once I get a proof of concept working with this, I’d like to move on to using an ARM MCU
The ARM devices (e.g. STM32 family) are much more powerful and more interesting and use less power too. I use Visual GDB with Visual Studio so can write and debug C code (or C++) in these boards with ease, the whole experience is superb for someone learning this stuff.
 

Ya’akov

Joined Jan 27, 2019
10,235
I’ve gone through the code and reworked the comments so that they focus on why instead of just repeating what the code already say. I think I’m following your advice better now, but can you @Ya’akov check and let me know if I’m really doing it right?

C:
/*
* UART_Echo_Test.c
* Purpose:
*   Verify UART functionality on ATmega8A using echo test.
*   (If a character is typed on terminal, same character is echoed back.)
*
* Hardware:
*   - MCU: ATmega8A
*   - Clock: 12 MHz external crystal
*   - UART Pins: TXD (PD1), RXD (PD0)
*   - USB–TTL converter connected to PC terminal
*
* Toolchain:
*   - Language: C
*   - Compiler: AVR-GCC (via Microchip Studio)
*
* Author : Developer
* Created: 29-09-2025
*/

#define F_CPU       12000000UL
#define BAUD        9600      
#define UBRR_VAL    ((F_CPU / (16UL * BAUD)) - 1)   // Datasheet formula ensures accurate baud

#include <avr/io.h>
#include <util/delay.h>

/* Bit masks to make register operations self-documenting */
#define RX_COMPLETE  (1 << RXC)
#define TX_READY     (1 << UDRE)
#define RX_ENABLE    (1 << RXEN)
#define TX_ENABLE    (1 << TXEN)
#define UCSRC_SELECT (1 << URSEL)
#define UCSZ_8BIT    ((1 << UCSZ0) | (1 << UCSZ1))

/**
* UART_Init
* Sets baud rate and frame format so that the MCU’s UART
* matches common PC terminal settings (9600 baud, 8N1).
*/
void UART_Init(void) {
    UBRRH = (unsigned char)(UBRR_VAL >> 8);
    UBRRL = (unsigned char)(UBRR_VAL & 0xFF);

    UCSRB = RX_ENABLE | TX_ENABLE;    // enable RX/TX so data can flow both ways
    UCSRC = UCSRC_SELECT | UCSZ_8BIT; // 8-bit frame matches standard terminal setup
}

/**
* UART_RxChar
* Blocks until data arrives,
* always gets a valid received byte.
*/
unsigned char UART_RxChar(void) {
    while (!(UCSRA & RX_COMPLETE)) {
        // Busy-wait until RXC flag signals data is available
    }
    return UDR;
}

/**
* UART_TxChar
* Blocks until the transmit buffer is free to ensure
* characters are not lost or overwritten.
*/
void UART_TxChar(char ch) {
    while (!(UCSRA & TX_READY)) {
        // Busy-wait until UDRE flag signals buffer empty
    }
    UDR = ch;
}

/**
* UART_SendString
* send status/debug messages
*      without calling UART_TxChar repeatedly.
*/
void UART_SendString(const char *str) {
    while (*str) {
        UART_TxChar(*str++);
    }
}

int main(void) {
    UART_Init();
    _delay_ms(100);  // allow terminal/USB-TTL to stabilize

    // UART_SendString("UART Echo Test Ready\r\n");

    while (1) {
        unsigned char c = UART_RxChar();  // wait for input from user
        UART_TxChar(c);                   // echo back for verification
    }
}
It does look better.

Understand that this is not a rule per se, it is a principle. If you have some reason to comment something that can be read in the code, there is no law of nature that turns that into a sin.

Think about why you comment code—to make things easier for the maintenance programmer. Be kind, the person whose sanity you preserve is very likely... you.

I hope the improvement in the utility of the comments is self-evident to you.
 
Last edited:

Ya’akov

Joined Jan 27, 2019
10,235
Understand, there are no rules other than those adopted by people and groups. You might work at Motorola and they will have a set of rules for commenting, variable naming, macro naming, file naming. They might have rules for how args are to be passed and the order of those args. They might have rules for function names, order of function within files, or even the number of files and so on.
If useless comments are part of a "standard" then they just become part of the requirements.

But the principle (not rule) that I explained above is one that allows for maximal ease of reading code for the maintenance programmer (or user of a library, &c.) and very frequently that is the same person who wrote the code.

The minimum effective commenting prevents the problem of "why did I do that?" But often I include sections of the documentation that define things like bitmasked register values and constants that, although they appear in the documentation, are much more helpful in place.

To help make the code more fluent, I will pick larger comment blocks below the code with references to it in place.

As far as satisfying mandatory code standards—of course you need to meet the requirements of the customer but as far as adopting a particular ruleset for your own use, I feel strongly that reviewing all potential systems and evaluating them in light of the goal of making code easier to maintain is the right way to establish a personal standard where principles and heuristics govern the choice, and rules are rare and are only applied where calculating the best option is too costly.
 

WBahn

Joined Mar 31, 2012
32,823
I know a few companies that have the policy that code should not be commented (except for standardized information at the top of each file), except as absolutely necessary (meaning that each comment has to be accepted during code review as being extremely valuable/important). When I first encountered that, I was quite taken aback. But the rationale actually makes sense, provided it is enforced during code reviews. Essentially, it adopts the position that code should be written so that it is sufficiently self-documenting, requiring very good use of variable and procedure names and structural organization that makes each code block's relevance to the task being performed extremely apparent. It zealously emphasizes readability over performance.
 

BobTPH

Joined Jun 5, 2013
11,515
transmission and reception are working correctly on my ATmega8A using a USB–TTL converter.
I hate to rain on your parade, but this UART “driver” will not age well.

The problem is that the program must be ready to read a character when it comes in and stalls until it receives it. You will soon find cases where characters coming in are lost because your program is not ready to receive them. The hardware can buffer one or a few characters, but after that they will be lost.

The UART module has interrupt capability that allows a driver to overcome that problem. An interrupt handler is activated when the hardware buffer us full and pulls the character(s) out as they come in and stores them in a larger software buffer.

Similarly, on output, the micro has to wait as each character goes out. Eventually, you will want a driver that buffers the output and returns immediately so the micro can do other things as the characters are being sent.

This is typical of many hardware I/O drivers. You really need to use interrupts to write a proper driver.

Go ahead and continue with what you have now, but you will eventually have to revisit this driver.
 

Thread Starter

Embededd

Joined Jun 4, 2025
153
My next plan was to write the I2C driver, but my RFID reader actually works over UART. So I thought it would make sense to test the reader first, and now it’s working. When I show a card, I can successfully get the card ID

C:
/*
* EM18_RFID_UART.c
* Purpose:
*   Interface EM-18 RFID Reader with ATmega8A via UART
*   and print scanned card ID to the serial terminal.
*
* Hardware:
*   - MCU: ATmega8A
*   - Clock: 12 MHz external crystal
*   - UART: 9600 baud, 8N1
*   - EM-18 RFID Reader TX → ATmega8A RXD (PD0)
*   - USB–TTL converter → PC terminal
*
* Toolchain:
*   - Language: C
*   - Compiler: AVR-GCC (via Microchip Studio)
*
* Author : Developer
* Created: 02-10-2025
*/

#define F_CPU       12000000UL
#define BAUD        9600
#define UBRR_VAL    ((F_CPU / (16UL * BAUD)) - 1)

#include <avr/io.h>
#include <util/delay.h>

/* Bit masks */
#define RX_COMPLETE  (1 << RXC)
#define TX_READY     (1 << UDRE)
#define RX_ENABLE    (1 << RXEN)
#define TX_ENABLE    (1 << TXEN)
#define UCSRC_SELECT (1 << URSEL)
#define UCSZ_8BIT    ((1 << UCSZ0) | (1 << UCSZ1))

void UART_Init(void) {
    UBRRH = (unsigned char)(UBRR_VAL >> 8);
    UBRRL = (unsigned char)(UBRR_VAL & 0xFF);

    UCSRB = RX_ENABLE | TX_ENABLE;
    UCSRC = UCSRC_SELECT | UCSZ_8BIT; // 8N1
}

unsigned char UART_RxChar(void) {
    while (!(UCSRA & RX_COMPLETE));
    return UDR;
}

void UART_TxChar(char ch) {
    while (!(UCSRA & TX_READY));
    UDR = ch;
}

void UART_SendString(const char *str) {
    while (*str) {
        UART_TxChar(*str++);
    }
}

int main(void) {
    unsigned char card[13]; // 12 chars from EM-18 + null terminator
    unsigned char i;

    UART_Init();
    _delay_ms(100);

    UART_SendString("RFID Reader Ready...\r\n");

    while (1) {
        // EM-18 always sends 12 characters per tag
        for (i = 0; i < 12; i++) {
            card[i] = UART_RxChar();
        }
        card[12] = '\0'; // null terminate for safe string printing

        UART_SendString("Card ID: ");
        UART_SendString((char*)card);
        UART_SendString("\r\n");
    }
}
1759404535494.png
 

Thread Starter

Embededd

Joined Jun 4, 2025
153
So I guess now I’ve test code for each module (LCD, UART, I2C, Reader )basically I can check if each one is actually working before I start putting everything together

C:
/*
* DS1307_I2C_UART_Test.c
*
* Purpose:
*   Test communication with DS1307 RTC over I2C and report status over UART.
*   .
*
* Hardware:
*   - MCU: ATmega8A
*   - Clock: 12 MHz external crystal
*   - UART: TXD (PD1), RXD (PD0)
*   - I2C: SCL/SDA to DS1307
*
* Toolchain:
*   - Language: C
*   - Compiler: AVR-GCC (via Microchip Studio)
*
* Author: Developer
* Created: 02-10-2025
*/

#define F_CPU 12000000UL          // MCU clock frequency
#define SCL_FREQ 100000UL          // Target I2C frequency (100 kHz standard)
#define TWBR_VALUE ((F_CPU / SCL_FREQ - 16) / 2)  // Calculate TWBR to get correct I2C timing
#define DS1307_ADDR 0x68           // 7-bit I2C address of DS1307
#define LED_PIN PB0                // Optional: LED for ACK indication

#include <avr/io.h>
#include <util/delay.h>

/* UART settings */
#define BAUD 9600
#define UBRR_VAL ((F_CPU / (16UL * BAUD)) - 1)
#define RX_COMPLETE  (1 << RXC)
#define TX_READY     (1 << UDRE)
#define RX_ENABLE    (1 << RXEN)
#define TX_ENABLE    (1 << TXEN)
#define UCSRC_SELECT (1 << URSEL)
#define UCSZ_8BIT    ((1 << UCSZ0) | (1 << UCSZ1))

/* Initialize UART to communicate with PC/terminal */
void UART_Init(void) {
    // Set baud rate so that bits are transmitted at correct speed
    UBRRH = (unsigned char)(UBRR_VAL >> 8);
    UBRRL = (unsigned char)(UBRR_VAL & 0xFF);

    // Enable receiver and transmitter so MCU can send and receive characters
    UCSRB = RX_ENABLE | TX_ENABLE;

    // Set frame format: 8 data bits, 1 stop bit
    UCSRC = UCSRC_SELECT | UCSZ_8BIT;
}

/* Send one character over UART */
void UART_TxChar(char ch) {
    while (!(UCSRA & TX_READY));   // Wait until transmit buffer is empty
    UDR = ch;                       // Write character to UART data register
}

/* Send null-terminated string over UART */
void UART_SendString(const char *str) {
    while (*str) UART_TxChar(*str++);
}

/* Initialize I2C hardware for communication with DS1307 */
void i2c_init(void) {
    TWBR = (uint8_t)TWBR_VALUE;            // Set bit rate for target SCL frequency
    TWSR &= ~((1 << TWPS1) | (1 << TWPS0)); // Prescaler = 1, ensures correct timing
}

/* Generate I2C START condition */
uint8_t i2c_start(void) {
    TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN); // Send START
    while (!(TWCR & (1 << TWINT)));                  // Wait until START is transmitted

    uint8_t status = TWSR & 0xF8;
    // Return 1 if START or repeated START acknowledged by hardware
    return (status == 0x08 || status == 0x10);
}

/* Generate I2C STOP condition */
void i2c_stop(void) {
    TWCR = (1 << TWSTO) | (1 << TWINT) | (1 << TWEN); // Release bus
}

/* Send device address on I2C and check for ACK */
uint8_t i2c_write_address(uint8_t address) {
    TWDR = address;                        // Load device address into data register
    TWCR = (1 << TWEN) | (1 << TWINT);    // Start transmission
    while (!(TWCR & (1 << TWINT)));       // Wait for completion

    // Return 1 if slave acknowledged, 0 if not
    return (TWSR & 0xF8) == 0x18;
}

int main(void) {
    UART_Init();     // Initialize UART to report status
    i2c_init();      // Initialize I2C bus
    _delay_ms(100);  // Wait for DS1307 to power up properly

    UART_SendString("DS1307 I2C Test Ready\r\n"); // Inform user that test started

    while (1) {
        if (i2c_start()) {    // Try to initiate communication
            if (i2c_write_address(DS1307_ADDR << 1)) {
                // If DS1307 acknowledges, report success
                UART_SendString("DS1307 ACK received!\r\n");
            } else {
                // If no acknowledgment, warn user (possible wiring or power issue)
                UART_SendString("No ACK from DS1307\r\n");
            }
            i2c_stop();       // Release the bus
        }

        _delay_ms(1000);      // Wait 1 second before trying again
    }
}
1759412713246.png
 

Thread Starter

Embededd

Joined Jun 4, 2025
153
So, now the real challenge begins. With this project, I want to understand what it really means for code to be modular, readable, maintainable, portable, and reusable basically, how these concepts apply when you’re actually working on a real embedded project. I’d really appreciate your guidance on how to structure my code and approach things the right way in this context

So right now I’m writing code for the ATmega8A, but later my controller might change let’s say to an ARM or (Atmega16 / Atmega 32, PIC). That mean I’ll have to rewrite everything from scratch. That sounds like a big headache, especially if we’re working on a bigger project. Isn’t there a way to write more universal’ software. The problem, of course, is that every MCU has a different architecture and different register configurations. I guess this can even be a problem with any device for example, if i move to a next version of the DS1307 or a different peripheral.

How do people usually tackle this?

When the code becomes really big, how can we figure out which specific function is causing an error or an exception? I mean, what’s the usual way to handle errors and exceptions in large embedded projects so we can debug efficiently?

@WBahn @MrChips @Futurist @BobTPH @Ya’akov @drjohsmith @Dave Lowther
 
Last edited:

Futurist

Joined Apr 8, 2025
748
If useless comments are part of a "standard" then they just become part of the requirements.

But the principle (not rule) that I explained above is one that allows for maximal ease of reading code for the maintenance programmer (or user of a library, &c.) and very frequently that is the same person who wrote the code.

The minimum effective commenting prevents the problem of "why did I do that?" But often I include sections of the documentation that define things like bitmasked register values and constants that, although they appear in the documentation, are much more helpful in place.

To help make the code more fluent, I will pick larger comment blocks below the code with references to it in place.

As far as satisfying mandatory code standards—of course you need to meet the requirements of the customer but as far as adopting a particular ruleset for your own use, I feel strongly that reviewing all potential systems and evaluating them in light of the goal of making code easier to maintain is the right way to establish a personal standard where principles and heuristics govern the choice, and rules are rare and are only applied where calculating the best option is too costly.
I wasn't referring specifically to commenting, that like other issues of style is often also governed by chosen standards though.

If one is delivering source code to a customer then yes, they might well have requirements there but in cases where source is not a deliverable then it is the team's standards that dictate.

The fact is there are always standards to adhere to sometimes they are just unstated (like when working in isolation).

Standards are an ever present reality and so ideally should be explicit and shared, the value of such standards is to protect the company's investment in creating source code, it has long term value in that code written ten years ago by people long since gone is as readable today to a team as it was ten years ago.

Having said all that, comments are a special case because they must always be manually maintained when code changes, I'm sure we've all seen large codebases where heavily worked on code has comments stemming from day one and are now out of step with the lates code, outdated, inaccurate comments are worse than none at all.
 
Top