Avr Software Uart In C

I've recently started work on a small project to provide visual notifications from a PC over a Bluetooth connection. I mentioned it on G+ a little while ago, it's essentially a minimalist version of this product.

Simple transmit-only UART driver This is a very simple C-based transmit-only UART, good for low speeds up to 4800 baud when used with a 1MHz clock. The intent of this is to provide a simple console output for debugging code on small ATTiny-series microcontroller, but in theory should work on any AVR chip. I expect most AVR developers are familiar with using PWM, where the output pin is toggled at a given duty cycle, independent of the code execution. The technique behind my full-duplex UART is using the waveform generation mode so the timer/counter hardware sets the OC0A pin at the appropriate time for each bit to be transmitted. The best solution that I have thinking of so far is to rewrite the half duplex interupt driven software UART example from Atmel (AVR304) in C using the same ways of signal handeling as in uart.c from AVRfreaks gcctest7 example. Sep 26, 2014 AVR serial communication UART tutorial using Embedded C language code program built AVR studio software AVR microcontroller tutorials embedded design system. This manual is designed to help embedded programmers and students, rapidly exploit the Avr(Atmega)-Controller for embedded applications. This manual has been targeted at embedded systems programmers and Students who have basic knowledge of Avr(Atmega32/Avr) architecture and C.

I started working on a post describing the work I was doing but it quickly expanded into something much larger than I expected as I started to describe aspects of my ATtiny85 template library which is being used as the basis of the project.

I decided to split the post into a number of smaller ones so I can cover the various topics in detail without swamping you with information. This post, the first in the series, provides an overview of the software UART implementation in the library - how it's implemented and how it's used. The upcoming posts will cover:

  1. Using a HC-05 breakout module with the library. This will include using AT commands to configure the module and basic connections to the ATtiny.
  2. PWM output on the ATtiny and the template libraries equivalent to the analogOut() function in the Arduino library.
  3. Finally the full project including schematics and code. This uses RGB LEDs to provide colour coded notifications under control of a remote PC through Bluetooth. Use it to provide information in a glance - you can control the colour based on whatever data you choose, stock information, website analytics or Bitcoin exchange rate.
Software

For now, a brief introduction to the library and how to use the software UART.

I've mentioned my ATtiny Breakout Board and the associated template library in previous posts but only on a very superficial level. Although this post will concentrate on the software UART implementation it's worth running over it again.

The library is available on GitHub at the link above and the documentation for it can be found here. I've done my best to provide enough information in the project documentation to make it easy to get started. I'd appreciate any feedback you might have on how to improve it.

To use the library as a basis for your own projects simply fork the GitHub project (or clone your own local copy) and start modifying the code in the firmware directory. To get started you only need to start modifying the file hardware.h (which contains the pin assignments and is used to enable or disable various parts of the library) and main.c.

To keep up to date with newer version of the library simply merge the master branch of the original GitHub library into your project every now and then. The majority of updates will be in the firmware/shared and firmware/include directories which contain the implementations and definitions of the library components respectively.

Before I get into the implementation details of the UART functions it's probably best to run through how serial communication appears on the wire. Because we are generating these signals in software it's a good idea to know what is expected and what sort of issues to look out for.

The diagram above shows the voltage levels on the Tx pin when the ASCII character sequence 'AVR' is being sent. Obviously this is what will be seen on the Rx pin as well on the receiver end of the connection. In this case the connection is running at 3.3V levels (not RS232 levels) and the connection is configured for 57.6 KBaud, 8 data bits, 1 stop bit and no parity (57600 8N1). I've marked each character on the diagram along with the values for the individual bits.

The diagram above shows the detail for a single character (in this case the ASCII character 'A'). Even though we are only sending 8 bits of data per character additional bits are required for framing (to allow the receiver to detect the start of a character and to determine it has reached the end). In every case there is a single start bit - this causes the communications line to transition from it's idle state (high) to low for at least the duration of a single bit. The data bits come next starting from the least significant bit (bit 0) to the most (bit 7) and finally, the stop bit. The stop bit ensures the line comes back up to it's idle state for at least one bit.

The width, or duration, of each bit is a function of the baud rate - for the speed we are running at that is 1/57600 seconds per bit (about 17 microseconds) - and each bit must be exactly the same width. For our configuration it takes 10 bits to transmit a single byte (1 start bit, 8 data bits and a stop bit) so it takes 170 microseconds per byte of data giving a peak transfer rate of 5.6Kb per second assuming there are no gaps between the bytes. In reality it will be lower).

From the first diagram you can see that there are no restrictions on the gaps between the characters (the inter-character delay). The line must remain at an idle state for this period (logic high) but the duration doesn't have to be a multiple of whole bits and can be as long as is needed.

Writing data out the serial port is fairly straight forward - we know the width of each bit so we simply toggle an output pin as needed and ensure it stays in the right state for the full duration of a bit. All we need to do is pull the output pin low for one bit width for the start bit, send out the individual bits from LSB to MSB and then bring the output high for one bit width as the stop bit. The output is then left high to represent the idle state.

Avr software uart in c language

The current implementation does just that in a small loop of assembly code. Because the timing is critical interrupts are disabled while this is happening - you will need to keep this in mind if you are depending on interrupts in your code.

NOTE: The serial send and receive code in the library makes heavy use of the assembly routines developed by Ralph Doncaster and described here. I said that generating the output is straight forward and, as a concept, it is. The actual implementation to get the timing right based purely on the number of instruction cycles needed to execute each op-code is another matter. The code developed by Ralph is a really nice piece of work - I highly recommend visiting his site for more examples.

Reading the serial data is the reverse operation. We simply watch the input pin and wait for it to transition from high (idle state) to low which will be our start bit. We then wait for 1.5 bit periods and then start sampling for the input pin once every bit period to get the value for the appropriate bit. We need 9 samples in all - 8 for the data and 1 for the stop bit.

Avr software uart in chinaAvr Software Uart In C

The reason for the 1.5 bit delay at the start is to ensure that we are sampling the bit value in the middle of it's transmission period. If we are slightly off in our timing we will still get the right value for the bit.

The full implementation of the receiver can be found here. Initially this only provided a blocking implementation - if you called the uartRecv() function it would wait indefinitely until a start bit was detected before it would read and return a character. In cases where nothing happens until it is requested over the serial port (like a bootloader for example) it doesn't matter that the processor is locked in an endless loop with interrupts turned off.

If you want to be able to do work and only respond to serial input when it was available it was not the perfect solution. I've now added an option to have the start bit trigger a pin change interrupt which will cause the incoming byte to be read and buffered for later processing. There is also a uartAvail() function that tells you how many characters are currently buffered.

Pin Change Interrupts

The AVR allows for an interrupt to be triggered when the value of an input pin changes state (in either direction). On the ATtiny85 all of the inputs can be used to trigger this interrupt (which uses the vector PCINT0) but, if you enable multiple pins as triggers, you can't tell which one actually caused the interrupt. You can figure out what the transition was though - if the current reading on the input is 0 you can assume a high to low transition for example.

When the interrupt handler is invoked interrupts will be disabled until they are explicitly enabled by the handler code or the interrupt handler returns. The interrupts don't queue so if another triggering event occurs while the current interrupt is being handled it won't be immediately triggered again when it is done.

Handling the Interrupt

Avr Software Uart In C Online

Supporting the interrupt is actually straight forward - what was the original code for the uartRecv() function has been moved to an ISR handler. The code to wait for the start bit is no longer necessary as it has already been detected to trigger the interrupt.