0
\$\begingroup\$

I am trying to implement a software UART for the ATtiny84A, as it does not come with a UART out of the box.

The following is an example of the UART TX:

DDRB  |= (1 << DDB0); // Set the pin on Port B0 to be an output for UART TX.
PORTB |= (1 << PORTB0); // Default the pin to HIGH for the idle high of the UART

void uart_tx(uint8_t *transmit_data)
{
    uint8_t string_length = strlen(transmit_data);
    for (uint_8t character = 0; character < string_length; character++) // Separate the string into characters.
    {
        PORTB &=~ (1 << PORTB0); // Send a start bit by bringing the UART TX low.
        timer_delay(); // Extra function that generates a delay to generate the appropriate baudrate.
        
        for (uint8_t character_bit = 0; character_bit < 8; character_bit++) // Separate the character into bits.
        {
            if ((1 << character_bit) & transmit_data[character]) // 
            {
                PORTB |= (1 << PORTB0); // Transmit a logical one
                timer_delay(); // Aforementioned delay
            } else {
                PORTB &=~ (1 << PORTB0); // Transmit a logical 0
                timer_delay(); // Aforementioned delay
            }
        }
        PORTB |= (1 << PORTB0); // Transmit a stop bit by bringing UART tx High.
        timer_delay(); // Aforementioned delay
    }
}

uart_tx("ab");

What I would expect as an output is

0100001101 0010001101

however, what I am actually getting is shown in the following UART Waveform On Oscilloscope

which in terms of bits is

00001000011010010001101...

Taken as a whole, it has little meaning, but upon closer inspection, parts of it are correct. What is wrong about it is the 000 inserted at the beginning, so a more accurate way of looking at it is

?...000 0100001101 0010001101
    ^ start bit? 

More specifically: The last two frames are accurate, but mystery data is being inserted at the beginning.

What is very strange, is that If I implement this in a regular C program, it works as I would expect:

#include <stdio.h>
#include <string.h>

void uart_tx(unsigned char *transmit_data)
{
    for (unsigned char character = 0; character < strlen(transmit_data); character++)
    {
        printf("0");
        for (unsigned char character_bit = 0; character_bit < 8; character_bit++)
        {
            if ((1 << character_bit) & transmit_data[character])
            {
                printf("1");
            } else
              {
                printf("0");
            }
        }
        printf("1");
        printf(" ");
    }
}

uart_tx("ab");

outputs

0100001101 0010001101

as expected, so I am very perplexed as to what is going on here.

EDIT: Here is the delay related code

// Initializing the timer
TCCR0A  |=  (1 << WGM01);
TIMSK0  |=  (1 << OCIE0A);
OCR0A   =   52;

// timer function
void timer_delay(void)
{
    TCNT0   =   0;  // Reset the time
    TCCR0B  |=  (1 << CS01); // start the timer with /8 prescaler.
    while (!(TIFR0 & (1 << OCF0A)));    // Wait until the compare interrupt flag is set
    TIFR0   &=~ (1 << OCF0A);   // Reset the Compare flag
    TCCR0B  &=~ (1 << CS01);    // Stop the timer.
}
```
\$\endgroup\$
12
  • \$\begingroup\$ The idle state is HIGH. But why is the leftmost one third of the trace low ? If the start bit starts with a HIGH->LOW transition, your trigger (blue triangle) seems to be set for a LOW->HIGH transition. I think the extra 000 is coming from outside the function uart_tx. i.e., bet ween the line PORTB |= (1 << PORTB0); and the actual call to the uart_tx function. Can you show the lines in the code between the initial idle state setting and call to uart_tx ? \$\endgroup\$
    – AJN
    May 5 at 8:13
  • \$\begingroup\$ @AJN There's nothing in between those that would have any effect. In my code all there is is the initialization of a timer, and the timer_delay() function definition. I've even experimented with putting the idle state immediately before the function call, and still the same. \$\endgroup\$
    – Kalcifer
    May 5 at 8:16
  • \$\begingroup\$ Is the observation persisting for multiple runs ? Perhaps some interrupt occurred just after the start bit was issued in the run where you captured the data ? \$\endgroup\$
    – AJN
    May 5 at 8:19
  • 1
    \$\begingroup\$ strlen(transmit_data); What if I want to send binary data instead of just text? \$\endgroup\$
    – Lundin
    May 5 at 13:59
  • 1
    \$\begingroup\$ "// Initializing the timer TCCR0A |= ..." ... "TCCR0B = " I don't know this part but aren't those 2 different timer channels? It would seem that you init timer A then use timer B. \$\endgroup\$
    – Lundin
    May 5 at 14:11
3
\$\begingroup\$

The timer interrupt-flag is reset incorrectly. An interrupt flag is reset by writing a 1 to it, and not a 0.

TIFR0 &=~ (1 << OCF0A);
       ^ Incorrect

It should be

TIFR0 |= (1 << OCF0A);

See Section 11.9.7 of the ATtiny24A/44A/84A datasheet (2020 version - Revision A) listed here

...OCF0A is cleared by hardware when executing the cor- responding interrupt handling vector. Alternatively, OCF0A is cleared by writing a logic one to the flag...

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.