0
\$\begingroup\$

I've been experiencing this weird behavior on an AVR ATmega328p.

What is happening is the following: I have written a firmware that makes use of USART, TIMER0 and interrupts. The firmware works as expected.

Here is the output from the USART when it behaves as expected:

USART
Timer0
Sched
IRQ
 tick
 tick
 tick

But as soon as I add a call to a function (namely Uptime_Create()) to its main(), execution stops at the call of Uptime_Create(). I assume that because I have calls to USART_Puts() after each component is initialized.

This is my main.c

/* Written for Rainbowduino (ATmega328p at 16MHz) */

#include "USART.h"
#include "Timer.h"
#include "Sched.h"
#include "Uptime.h"
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

static USART uart;

static void uart_init(const uint32_t baud)
{
    uart = USART_Create(&UCSR0A, &UCSR0B, &UCSR0C, &UBRR0L, &UBRR0H, &UDR0);
    USART_SetBaudRate(uart, 38400, F_CPU);
    USART_Enable(uart, 1);
    USART_Puts(uart, "USART\r\n");
}

static void timer_init(void)
{
    Timer t0;
    t0 = Timer_Create(0, &TCCR0A, &TCCR0B,
                &TCNT0, 0,
                &OCR0A, 0,
                &OCR0B, 0,
                0, 0,
                &TIMSK0, &TIFR0);
    Timer_SetOverflowInterrupt(t0, 1);
    Timer_SetClockSource(t0, 3); /* 64 */
    USART_Puts(uart, "Timer0\r\n");
}

static void testTask(void)
{
#if 0
    char utstr[9];
    Uptime_Tick();
    Uptime_ToStr(utstr);
    USART_Puts(uart, utstr);
#endif
    USART_Puts(uart, " tick\r\n");
}

static void sched_init(void)
{
    Sched_Create();
    Sched_AddTask(testTask, 1000, 0);
    USART_Puts(uart, "Sched\r\n");
}

int main(void)
{
    uart_init(38400);
    timer_init();
    //Uptime_Create();
    sched_init();
    sei();
    USART_Puts(uart, "IRQ\r\n");
    while (1)
        Sched_Dispatch();
}

ISR(TIMER0_OVF_vect)
{
    Sched_Update();
}

The complete source code is available at github (http://github.com/lazlo/). The main.c was taken from the "lzoOS-test-firmware" repository from the sched/ directory.

In order to build the main.c you will have to check out the "Drivers" and the "lzoOS" repository as well and place them in the same directory.

mkdir tmp
cd tmp
git clone https://github.com/lazlo/Drivers.git
git clone https://github.com/lazlo/lzoOS.git
git clone https://github.com/lazlo/lzoOS-test-firmware.git
cd lzoOS-test-firmware/sched
make

As soon as the call to Uptime_Create() is active again, the firmware stops execution (IMHO) after "Timer0" is printed out by USART_Puts().

I have already compiled a version with and without the call to Uptime_Create() and compared the assembly listing (.lss files). From what I can see only offsets differ (as Uptime.o is always linked to the firmware, no matter if the call is made or not).

What Uptime_Create() actually does is to initlaize a static struct (inside of Uptime.c) with zero.

Here is the code form Uptime.c

typedef struct UptimeStruct
{
    uint8_t seconds;
    uint8_t minutes;
    uint8_t hours;
} UptimeStruct;

static UptimeStruct uptime;

void Uptime_Create(void)
{
    uptime.seconds = 0;
    uptime.minutes = 0;
    uptime.hours = 0;
}

Any suggestions welcome!

UPDATE: I made a little experiment where I only use the USART and Uptime code. It looks like this:

#include "USART.h"
#include "Uptime.h"
#include <avr/io.h>

int main(void)
{
    USART u;

    u = USART_Create(&UCSR0A, &UCSR0B, &UCSR0C, &UBRR0L, &UBRR0H, &UDR0);
    USART_SetBaudRate(u, 38400, F_CPU);
    USART_Enable(u, 1);
    USART_Puts(u, "USART\r\n");

    Uptime_Create();
    USART_Puts(u, "Uptime\r\n");

    while (1)
        ;
}

Turns out, the strinf "Uptime" is never printed. Again I looked at the assembly listing but wasn't able to make sense or see the problem that would make the controller halt. What happens in Uptime_Create() looks like this:

00000384 <Uptime_Create>:

static UptimeStruct uptime;

void Uptime_Create(void)
{
    uptime.seconds = 0;
 384:   10 92 18 01     sts 0x0118, r1
    uptime.minutes = 0;
 388:   10 92 19 01     sts 0x0119, r1
    uptime.hours = 0;
 38c:   10 92 1a 01     sts 0x011A, r1
 390:   08 95           ret

00000392 <Uptime_Destroy>:
}

UPDATE: When I was asked to answer if Uptime_Create() ever actually returns (on the microcontroller) I wrote a new minimal firmware that only uses the USART and Uptime module. While running this firmware in Simulavr, I noticed, Uptime_Create() returns but USART_Puts() makes code execution stop/run an endless loop. Specifically it is BitIsSet() in USART_Putc() that checks if UDRE (USART Data Register Empty) bit is set. Weird enough, before I call Uptime_Create() I call USART_Puts(uart, "USART ready\r\n"); which works and I can see the output. But after calling Uptime_Create() the next call to USART_Puts() results in an endless loop because the UDRE in the UCSRA (USART control and status register) is never set.

Why this is, I can only guess. As i tried to set a data watchpoint using gdb in the simulator but as it seems the simulator does not support data watchpoints.

UPDATE: I found the reason: Uptime_Create() initializes "static UptimeStruct uptime" zero, which turns out to be at the same memory address as "static USARTStruct usart[0]". Which in turn results in setting the pointers in usart struct that point to the registers (UCSR[A..C] and so on) to zero.

\$\endgroup\$
12
  • 1
    \$\begingroup\$ You need to include the called functions in the question itself. Without that this will probably be closed as unanswerable. \$\endgroup\$ Commented Jun 30, 2017 at 16:35
  • \$\begingroup\$ makefile:41: ../CppUTest/build/MakefileWorker.mk: No such file or directory \$\endgroup\$ Commented Jun 30, 2017 at 17:03
  • \$\begingroup\$ @BruceAbbott: You used the CppUTest from the debian package. That one is missing the MakefileWorker.mk : ( Try the cpputest git repository from github. That one should work \$\endgroup\$
    – lazlo
    Commented Jun 30, 2017 at 17:22
  • \$\begingroup\$ @ChrisStratton Added the code from Uptime.c \$\endgroup\$
    – lazlo
    Commented Jun 30, 2017 at 17:25
  • \$\begingroup\$ Try using a debugger to step through the execution... My guess is you are having a hardfault somewhere in that create line. Have you tried using a debugger and breakpoints to find out what line of code in USART_CREATE is causing the lockup? \$\endgroup\$
    – gregb212
    Commented Jun 30, 2017 at 17:39

1 Answer 1

5
\$\begingroup\$

I suspect that USARTStruct and UptimeStruct are overlapping in memory, so when Uptime_Create() initializes the time it wipes out part of USARTStruct and the UART transmit code gets stuck waiting on the wrong register.

In USART.c you have this:-

typedef struct USARTStruct
{
    volatile uint8_t *ucsra;
    volatile uint8_t *ucsrb;
    volatile uint8_t *ucsrc;
    volatile uint8_t *ubrrl;
    volatile uint8_t *ubrrh;
    volatile uint8_t *udr;
} USARTStruct;

static USARTStruct usart[0];

usart[0] is an array of size zero. Change it to static struct USARTStruct usart[1] and the compiler should allocate sufficient space for the structure.

\$\endgroup\$
4
  • 3
    \$\begingroup\$ This is why questions must include the code, digging around in the asker's github to find a mistake like this should not be required. \$\endgroup\$ Commented Jul 2, 2017 at 3:02
  • \$\begingroup\$ @ChrisStratton: You are right! This was my 2nd question, so I guess there is chance to improve. \$\endgroup\$
    – lazlo
    Commented Jul 3, 2017 at 7:51
  • \$\begingroup\$ @BruceAbbott Damn! You are right! I just found out that addresses of uptime and usart overlap. But I didn't get to the point where I noticed usart to be of length 0! Thank you sooo much! \$\endgroup\$
    – lazlo
    Commented Jul 3, 2017 at 7:52
  • \$\begingroup\$ @lazlo Of course, what you hope to accomplish by mocking up the USART register structure in RAM remains unclear. To actually access a USART, you need to use the physical addresses of these registers. If you want a struct that reflects those, this is not that. \$\endgroup\$ Commented Jul 3, 2017 at 12:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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