📜 ⬆️ ⬇️

Micro-UART for Microcontroller (ATtiny13A)

I am glad to welcome again all visitors to this glorious place on the Internet to bring one more modest contribution.

This time, it concerns the already rather hackneyed topic, around which there are always disputes, and when it comes to the point that all its implementations are either sharpened for a specific task, by a specific person, under specific conditions, or do not work at all.


It will be about the software implementation of UART , for Atmel AVR microcontrollers, the intellectual property of which for some time has been owned by Microchip .

So, following the tradition, the saying


The thought in my head on the topic of this article has crept in for a long time, thanks to one dispute in the comments that, they say: the software UART for a microcontroller working from an internal generator is very unstable and you cannot get high accuracy at speeds above 9600 baud because of the strong drift setting frequency of this internal oscillator.
Then one day, I again wanted to check this thesis (unfortunately I could not find any article where this was discussed, no discussion. Although I am sure that this was happening here on Habré), find out:


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 size of the code on the Anarchic electronics channel of the Telegram messenger, and suddenly the topic began to resonate. New people began to join the discussion, and soon the dispute returned to its normal course, the user of the channel @ RT_1_98 - very fiercely insisted on the failure of this work (for which we thank him separately, he only inflamed me more - to prove the opposite;)) Meanwhile, one of channel administrators @Byte_kgd, suggested not to stop there, but bring the work to its logical conclusion: write a receiver to the transmitter too!
')
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 by other people, so that the code is easy to read, and thanks to the simplicity of the idea, it could be modified by other microcontrollers. Therefore, I formed calibration delay constants for a group of frequencies from 9600 to 57600 baud, and wrote a transmitter function that sends a data byte to the line, completely refusing to interrupt the timer.

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

The performance has inspired me a lot, since I have already met the code size, less than any other implementations.

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

I collected my thoughts for a long time ...


One main thought haunted me: since I decided to abandon the timer interruptions 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 entire reception takes place in one call.

Yes, this is bad practice, and yes - this is indecent behavior! But firstly, the microcontroller's memory does not allow storing a large buffer, which is expensive for the main code, and secondly, I decided to write a handler so that it can process:


When writing the interrupt code was completed, I conducted tests for the stability of the reception / transmission, having built such a model in Proteus:


One microcontroller sent a predefined 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 one, having accepted it, compared it with the reference one, and in case of inconsistency, sent a message to the second terminal: “ Error! ”, Then started the simulation and went to sleep ...



... in the morning


when I woke up I was disappointed: the terminal was littered with unwanted error messages ... But when I counted them, everything turned out to be 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 mismatch of the buffer).

... So this is a 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.

Having revised the code of the receiver and transmitter again, I again reduced the transmitter to 60 bytes (30 commands), and optimized the receiver.

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

And he gloatingly and pathetically told me: "... safsem narabotait! Wow! ..."


That's so fiasco, bro ...


... The new year has come, the whole Olivier has been eaten, all the champagne has been drunk, the trees are already burning outside the window.
And the code does not work ... it doesn't give in to any!

And finally it comes to me that in the code it is necessary to cross out half of it, rewrite half, and the third half needs to be turned inside out.

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

First, the transmitter code was reduced to 54 bytes (26 commands), second, the fatal interruption error was eliminated: the flag was not reset (Proteus digested it without even reporting to the log. And this is the second inaccuracy in the simulation, which I found for MK ATtiny13A), in the third and fourth - architectural errors were eliminated, such as: incorrect return of the flag, jitter, mismatch of the duration of transmission and reception, etc. The receiver code was rewritten with self-synchronization using the STOP bit, which undoubtedly improved the quality of reception.

In addition, I finally got a logic analyzer, uncovered a programmer, a UART converter, and took the precious ATtiny13A from my bins, decided FIRST for all this time, to analyze the performance in the hardware!

Looking ahead to say: MIRACLE HAPPENED!

The stitched “tinka” showed “ping” in the terminal, and answered with a smile: Test012345.
The use of a logic analyzer showed a discrepancy in delays in Proteus and on real hardware. Due to this, we managed to unify the delay by one constant for the receiver and for the transmitter:



It also allowed us to find the theoretical limit for the maximum transmission rate under normal conditions:



I deliberately did not choose another screen, showing receiver errors that could not work at such speeds, due to the fact that the execution of the prologue and the epilogue in the interruption spell , 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 due to objective reasons, I decided to leave the code as it is, with a restriction on reception / transmission of 250 kbaud.

Total


As a result of this very hard work,


This image clearly shows that the demo example uses the entire available set of functions, with a total size of the demo project - 920 bytes of occupied flash memory. With proper organization, you can use only the necessary functions, which will greatly minimize the cost for the main project, while allowing you to debug the microcontroller through the UART. The code is easily modified for other controllers (for example - ATtiny85A), while allowing you to increase the buffer size and the number of functions used, not to the detriment of the performance and efficiency of the main task. The code was not submitted to the library, for clarity and ease of analysis and training. It was decided not to fully implement the buffer overflow control. This means that during an overflow, the overflow flag is not set, instead an interrupt exit occurs, and the subsequent behavior determines the incoming flow.

Thus, the microcontroller again goes into the interrupt and overwrites the received buffer with new data. To avoid this, it is not necessary to feed it with portions of data that are longer than a certain buffer size. It was decided to stop at 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 work in normal mode.

Another deviation from the tradition at this time was the lack of code excerpts in the form of screenshots. In the last article in the first comment there was anger on this topic :)
And if so, then let the code lie on Githab, and as a result, display all the beauty in a form that is not formatted by it:

Draft version 2.0 for Atmel Studio 7.0

This is the second version of the project, and the last. Any changes that may be made will apply only to calibration delays. At the moment, for the entire range, tests have been performed and successfully completed.

I wish you a speedy heat attack in your homes, on your streets and in your hearts!
All successes, creations, achievements!

The use for commercial purposes, the resale of the source code, the use for profit and any mercenary purposes is prohibited. Source texts are distributed free of charge as they are, in case of use on other sites or other sources, the indication of the author and notification of placement is a must!

Source: https://habr.com/ru/post/346980/


All Articles