Back to Home

Micro-UART for Microcontroller (ATtiny13A)

UART · ATtiny13 · Inline Assembler · atmel studio

Micro-UART for Microcontroller (ATtiny13A)

    I am glad to welcome again all visitors to this glorious place on the Internet to make another modest contribution.

    This time, it concerns a rather hackneyed topic, around which there is always controversy, and when it comes to the matter it turns out that all its implementations are either tailored for a specific task, by a specific person, in specific conditions, or do not work at all.


    It's about the software implementation of the UART , for microcontrollers AVR of the Atmel , the intellectual property that for some time now owned by Microchip .

    So, following tradition, saying


    The thought in my head on the topic of this article crept in a long time, thanks to one argument in the comments that, they say: the software UART for a microcontroller that runs on an internal generator is very unstable, and you cannot get high accuracy at speeds above 9600 baud, due to the strong drift of the frequencies of this very internal generator.
    And once, I again wanted to check this thesis (unfortunately I could not find any article where it was discussed, nor discussion. Although I am sure that this was happening here, on Habré), find out:

    • How much can I reduce the error;
    • At what maximum transfer rate will the device be able to communicate;
    • Will I be able to compress the code using all my knowledge.

    Thus, the initial meaning of this work was rather a sporting interest.
    But when the first results were obtained, I decided to demonstrate the initial code size on the Anarchistic electronics channel of the Telegram messenger, and suddenly the topic began to resonate. New people began to join in the discussion, and soon the dispute returned to its usual course, the user of the channel @ RT_1_98 - very fiercely insisted on the insolvency of this work (for which special thanks to him, he only inflamed me harder - prove the opposite;)) Meanwhile, one of the channel’s administrators, @Byte_kgd, offered not to stop there, but to bring the work to its logical conclusion: write a receiver to the transmitter as well!

    And the challenge was accepted ...

    Without further ado, I chose a simple path.
    No, not because I'm afraid of difficulties, but because I also wanted the results to be applicable to other people, to make the code easy to read, and because of the simplicity of the idea, it could be modified to fit other microcontrollers. Therefore, I generated calibration delay constants for a group of frequencies from 9600 to 57600 baud, and wrote a transmitter function that sends data bytes to the line, completely abandoning timer interrupts.

    Of course, I sat down to write in my favorite IDE - Atmel Studio . In the original version, the function body code (in inline assembler) took about 90 bytes (45 instructions), thanks to which I set the delay constants, testing the execution in the Proteus simulator.

    Efficiency inspired me very much, since I already met the code size, less than any other implementations.

    With a little thought, I rewrote the function for sending strings ending in zero, which allowed sending a pre-prepared buffer of a certain size with the #define directive, and in addition, I managed to reduce the code size to 76 bytes (38 commands)! At this point, I decided to show the test results in Telegram, after which it was decided to write a receiver.

    I gathered my thoughts for a long time ...


    One main thought haunted me: since I decided to abandon timer interrupts in the transmitter, it would be nice to build a receiver on the same principle ... but how ?!
    And I decided on a desperate step: write an interrupt handler in such a way that the whole reception happens in one call.

    Yes, this is bad practice, and yes - this is obscene behavior! But firstly, the memory capacities of the microcontroller do not allow storing a large buffer, this is unprofitable for the main code, and secondly, I decided to write a handler so that it could handle:

    • Buffer overflow (implemented by exiting the interrupt when the border is reached);
    • A break in the reception line (test emulation in Proteus, showed a complete hang, as a result of which a decision was made to control this condition);
    • And, in fact, regular behavior (with filling in the flag for successful data reception);

    When the writing of the interrupt code was completed, I conducted tests for receiving / transmitting stability by building such a model in Proteus:


    One microcontroller transmitted a predetermined buffer with test contents:

    char Rx_Buf[Buf_Size]={'T','e','s','t','0','1','2','3','4','5',10,13,0};

    And the second in the meantime, having accepted it, compared it with the reference one, and in case of discrepancy, sent the message “ Error!Via the data line to the second terminal , after which it started the simulation and went to bed ...



    ... in the morning


    when I woke up I was disappointed: the terminal was strewn with unwanted error messages ... But when I counted them, everything turned out not so bad: for 9 hours of work, at a speed of 57600 baud there were 11 messages. This means that for 3888000 bytes, no more than 143 errors occurred (in the case of a complete buffer mismatch).

    ... So this is success! ...


    Indeed, the probability of error turned out to be several orders of magnitude lower, and I attributed this to the high accuracy of the Proteus simulation.

    After reviewing the receiver and transmitter code again, I again reduced the transmitter to 60 bytes (30 instructions), and optimized the receiver.

    Then he passed the code to @Byte_kgd, which ardently wanted to participate in testing, with a slight digression: only ATtiny2313 is available.

    And he told me gloatingly and pathos: "... the safsem is unemployed! Vaapsche! ..."


    That’s a fiasco, bro ...


    ... The New Year has arrived, the whole olivier has been eaten, all the champagne has been drunk, the Christmas trees outside the window are already burning.
    But the code doesn’t work ... it doesn’t lend itself to anything!

    And finally it dawns on me that in the code you need to cross out half, half rewrite, and the third half need to be turned inside out.

    And then Ostap suffered! (c) I. Ilf E. Petrov.

    Firstly, the transmitter code was reduced to 54 bytes (26 instructions), and secondly, a fatal interrupt error was eliminated: an unset flag (Proteus digested it without even reporting to the log. And this is the second inaccuracy in the simulation, which I found for ATtiny13A MK), in the third and fourth - architectural errors were eliminated, such as: incorrect flag return, jitter, mismatch between transmission and reception durations, etc. The receiver code was rewritten with self-synchronization according to the STOP bit, which undoubtedly improved the reception quality.

    In addition, I finally pulled out a logic analyzer, uncovered the programmer, UART-converter, and taking the precious ATtiny13A out of my bins, I decided for the FIRST TIME in all this time to analyze the performance in hardware!

    Looking ahead, I’ll say:MIRACLE HAPPENED!

    The stitched “tinka” showed “ping” in the terminal, and answered with a smile: Test012345.
    Using a logic analyzer showed a discrepancy in delays in Proteus and on real hardware. Thanks to this, it was possible to unify the delay with one constant for the receiver and for the transmitter:



    It also allowed



    me to find a theoretical limit for the maximum transmission speed under normal conditions: I deliberately did not choose another screen, showing errors of the receiver that could not work at such speeds, due to the fact that the execution of the prologue and epilogue in the spellinterrupt, have time to skip the start bit. However, in a slightly modified version, I was able to achieve reliable reception at a speed of 512 kbaud, but for objective reasons, I decided to leave the code as it is, with a restriction on the reception / transmission of 250 kbaud.

    Total


    As a result of a very exhausting work,

    • The result is the most compact code in the world!
    • Non-maskable (INT0 and INT1) interrupts
    • Not using timer interrupts , freeing up all available PWM channels
    • Allows you to flexibly operate the port settings , choosing the necessary pins for the send and receive lines
    • Allows you to stably transmit and receive data at speeds from 9600 to 250,000 baud
    • Transfer lines from flash memory , thanks to the corresponding function
    • Convert and transmit data in binary, hex, and decimal (for the latter, two functions for transferring bytes and data words are implemented), thanks to the corresponding functions

    In this imageit is clearly seen that the demo uses the entire available set of functions, with the total size of the demo project - 920 bytes of used flash memory. With proper organization, you can use only the necessary functions, which will greatly minimize costs for the main project, while allowing you to debug the microcontroller via UART. The code can be easily modified for other controllers (for example, ATtiny85A), while allowing you to increase the size of the buffer and the number of functions used, without compromising the performance and performance of the main task. The code was not submitted to the library, for clarity and convenience of analysis and training. Buffer overflow control was decided not to be fully implemented. This means that during overflow the overflow flag is not set, instead, the interrupt is exited,

    Thus, the microcontroller again goes into interruption and overwrites the received buffer with new data. To avoid this, you do not need to feed it with chunks of data that are longer than a certain buffer size. It was decided to stop on this, after several discussions, so as not to inflate the handler code. The main condition is met - the microcontroller does not freeze and continues to operate normally.

    Another departure from tradition this time was the lack of code excerpts in the form of screenshots. In the last article, the very first comment on this topic was angry :)
    And if so, then let the code lie on Githab, and as a result, display all the beauty in an unformatted form:

    Project version 2.0, for Atmel Studio 7.0

    This is the second version of the project, and the latest. All changes that can be made will only concern calibration delays. At the moment, tests for the entire range have been carried out and successfully completed.

    I wish you an early onset of warmth, in your homes, on your streets and in your hearts!
    All success, creations, achievements!

    Commercial use, resale of source code, use for profit and any mercenary purposes is prohibited. The source texts are distributed free of charge as is, in case of use on other sites or in other sources, the author’s indication and notification of placement is mandatory!

    Read Next