1. Kontrola nad kodem w przypadku C jest niemal taka sama jak w asm.
2. C to jest w zasadzie "strukturalny asembler"

i do mikrokontrolerów wcale nie trzeba go naciągać. Różne rodzaje pamięci w AVR narzucają konieczność kontroli - w asm także. I to jest poza standardowym C.
3. Dyrektywa "include" w C działa tak samo, jak w asemblerze, jeżeli ten ostatni coś takiego wspiera - tworzy jeden wielki plik źródłowy zawierający w miejscach jest wystąpienia "wklejoną" zawartość odpowiednich plików. Wielokrotnie i/lub w sposób zagłębiony, jeżeli tak zażądał programista.
4. Aby uniknąć wielokrotnych definicji przy wielokrotnym inkludowaniu tego samego pliku (problem pojawia się przy wielopoziomowym stosowaniu include) stosuje się technikę, o której wspomniał
janbernat.
5. Czy korzystałeś przy programowaniu w asemblerze z linkera i możliwości kompilowania programu z oddzielnych modułów? Prawdopodobnie nie, to by wyjaśniało niektóre Twoje problemy.
6. Jeżeli program składa się z kilku niezależnie kompilowanych modułów i odwołują się one wzajemnie do siebie, to trzeba poinformować kompilator o symbolach w innych modułach. Do tego właśnie służą deklaracje - deklaracje zmiennych "extern" i prototypy funkcji.
7. Aby umożliwić efektywne korzystanie z techniki opisanej w punkcie powyżej, przyjęło się umieszczać w plikach nagłówkowych tylko definicje typów i deklaracje, w dodatku obejmując cały plik przez #ifdef, jak opisał
janbernat i nie inkludować plików ".c". Nie jest to wymaganie kompilatora, ale narzędzia programistyczne (IDE) są do takiego postępowania przystosowane, więc nie należy się dziwić, jak nie działają prawidłowo przy nietypowym podejściu.
8. Definicji (tak zmiennych jak i funkcji) nie należy umieszczać w plikach nagłówkowych aby nie powodować wielokrotnego ich umieszczania w kodzie, co powinno być oczywiste, jeżeli uwzględnić to, co napisałem w punkcie 3 powyżej. Nie dotyczy to tylko funkcji inline.
--edit--
Moja odpowiedź to oczywiście do autora, posta kolegi
tmf jeszcze nie było, jak pisałem.