Getting Started with Baremetal Arduino C Programming | No IDE Required [Linux SDK]

Поделиться
HTML-код
  • Опубликовано: 27 окт 2024

Комментарии • 121

  • @michaelward2987
    @michaelward2987 4 года назад +159

    Great tutorial on this. I'm a retired low level C programmer (did lots of automation and motion control) and have just bought an Arduino to play with and was a little annoyed by all the high level calls to IDE functions, since I normally wrote those myself for whatever device I was working on. You just showed me how to get down to the level of coding that I wanted. Thanks!

    • @LowLevel-TV
      @LowLevel-TV  4 года назад +26

      That's exactly how I felt! I'm glad you learned something. Thanks for watching.

    • @mazzonijacopo
      @mazzonijacopo 2 года назад +3

      Is atmel studio still a thing? They had great documentation, before the arduino I used to use it to program the avr butterfly and it was great.

    • @rijo1254
      @rijo1254 7 месяцев назад

      So you are wasting time by reinventing wheel 😅

    • @arjayonan2953
      @arjayonan2953 7 месяцев назад

      @@rijo1254nah it's not a time wasted. Wasting time is using all high abstraction and not knowing enough how those works on low level and the first thing you will do when a bug occured is to google it.

    • @4DRC_
      @4DRC_ 7 месяцев назад +3

      @@rijo1254 it’s not reinventing the wheel, programming at the register level is extremely useful particularly for low power operation, such as with the MSP430

  • @padalavamsiujpnquxgri3475
    @padalavamsiujpnquxgri3475 7 месяцев назад +7

    In the second build command, avr-gcc -o main.o main.bin, it's crucial to specify the target frequency and controller (-DF_CPU=16000000UL -mmcu=atmega328p) to avoid unexpected behavior. Additionally, if the -c flag is removed from the first command, the second command becomes unnecessary, especially when dealing with single c files or libraries.

    • @ruthyputhy7498
      @ruthyputhy7498 3 месяца назад +1

      I've been struggling to bug fix some weird behavior in my program and this is what fixed it. Thanks!

  • @andriusp1984
    @andriusp1984 4 года назад +73

    This tutorial is what I wanted to try for a while: getting out from arduino IDE and doing things in terminal. And, after watching I did this. With small modifications in makefile I ran same Blink example under cheap clone based on Nano with 168p. That hooked me to dive deeper. I hope more low level arduino videos from you in future :)

    • @LowLevel-TV
      @LowLevel-TV  4 года назад +11

      Awesome! I'm happy to hear you were able to get this to work on other MCU's. I'll definitely be going deeper into bare metal AVR programming in future videos.

  • @noexpert1481
    @noexpert1481 3 года назад +18

    Holy shit how do you not have a billion followers! This is the best example of Direct to register programming I've seen

  • @fredesch3158
    @fredesch3158 3 года назад +22

    Please do more of this, I really enjoyed the video, but this is one of my first contacts with actual c code for arduino so it's really hard (for me at least haha), and you're tutorials are amazing

  • @ruffrecords
    @ruffrecords 2 года назад +4

    Great tutorial. I got this up an running in an evening using my new Raspberry Pi 400. All the AVR & GCC tools required are available in the Raspberry Pi repositories.

  • @DonQuichotteLiberia
    @DonQuichotteLiberia 3 года назад +6

    You are really good at this, the tutorials are clear and concise and exceptionally well explained. I hugely enjoy that format.

  • @edgarbonet1
    @edgarbonet1 3 года назад +7

    Hi! Nice video. Just a couple of comments:
    1. May be nitpicking but, _delay_ms() does not do a “nop” in a loop: it compiles to an empty loop. You may find a nop (and maybe an “rjmp .” or two) *after* the loop if the delay is not a multiple of the cycle duration, but the body of the loop is empty.
    2. Recent versions of avrdude can read directly the elf file. No need to convert it to ihex.

  • @voytechj
    @voytechj 3 года назад +7

    One issue is that output file should be named "led.elf", because that is a format coming from "gcc -o" command. "bin" extension suggest that content from this file can be directly copy to Flash memory at address 0 in a microcontroller. You can extract bin format using "avr-objcopy -O binary" option.

  • @andrewandrosow4797
    @andrewandrosow4797 Месяц назад

    It is the best approach! I worked with AVR MCUs until 2015, and I had not used Arduino. THe peripheral and interrupt system is quite easy (incompare to ESP32 or STM32) and the code with inner peripherial devices may be written without any libraries. But when you use external peripherial devices - the libraries may be neccessary.

  • @axelandru9346
    @axelandru9346 5 месяцев назад +1

    We learned the fundamentals of controlling a port (pin) in this case, of an arduino shell, which is amazing. All we have to do is imagine the opportunities from here !

  • @EulenBuch
    @EulenBuch 7 месяцев назад

    There could not have been a better video explaining the concept. Really amazing. Thank you.

  • @DMGInfinityWarrior
    @DMGInfinityWarrior 2 года назад +5

    thank god for this video! i had just gotten this and I know what you meant when you said it felt like python, all these built in functions that basically keep the actual functionality to seem like magic

    • @flflflflflfl
      @flflflflflfl 9 месяцев назад

      So setting a bit in some register to 1 to make an LED turn on isn't magic to you?

  • @ItsRyanStudios
    @ItsRyanStudios 2 месяца назад

    I'm moving from high level ML/ web dev to embedded dev/ ML.
    I was messing with Arduino, and was like what the heck are all of these high level functions??
    I may as well use python at that point.
    Very awesome to come across some true low level tutorials for Arduino

  • @Teklectic
    @Teklectic 2 года назад +4

    This is great! When I got started with Arduino I felt that Arduino C was a crutch and there was really no resources to help transition into proper low-level coding for the AVR, I think this is a great introduction into the absolute basics for those that want to start making more performant code and get the most out of their AVRs. Arduino definitely has it's place as a teaching aid, but I think they really need to work on a proper learning path for transitioning into proper code.

  • @jeffschroeder4805
    @jeffschroeder4805 Год назад

    I am always amazed at how simple the operation of a chip like this really is. I don't in any way suggest that simple means easy, the familiarity with the underlying operations requires a lot of time at the keyboard. Thank you

  • @ProVrakian
    @ProVrakian Месяц назад

    Thank you.
    This tied a lot of concepts together for me. More like this, please.

  • @colydeane
    @colydeane 4 года назад +4

    Nice one, I enjoyed watching this and adding to my Arduino knowledge base. Thanks.

    • @LowLevel-TV
      @LowLevel-TV  4 года назад

      Awesome! I'm happy you learned something.

  • @John-cg1sx
    @John-cg1sx 5 месяцев назад

    Amazing video mate. Watching this and listening to take it easy by the eagles was a unique experience reminding me of better days. I need to do something like this soon.

  • @metamud8686
    @metamud8686 2 года назад +3

    PORTB ^= (1

    • @SpeccyMan
      @SpeccyMan 2 месяца назад

      Simple elegance has been lost in the art of programming. I've seen terrible things done with Arduino. One of the worst was someone actually putting a delay() loop IN an interrupt service routine!

  • @moveaxdwordptrcrow7712
    @moveaxdwordptrcrow7712 3 года назад +4

    The best tutorial ever, you helped me a lot. Thank you

  • @allawhussein
    @allawhussein 2 года назад

    I really hope this video turns into a series

  • @djpenton779
    @djpenton779 Год назад

    Nice. I am a retired software engineer with no background in microcontroller programming. I recently started doing some arduino stuff but have been very uneasy about the amount that this (admittedly helpful) framework hides. Better to have a deeper understanding....

  • @Sevenhens
    @Sevenhens 3 года назад

    Just discovered this channel! This is awesome, looking forward to more of these videos!

  • @terezip2213
    @terezip2213 2 года назад +10

    I love bitwise operations... you can replace the set/unset code with a simple XOR to repeatedly flip the bit! Then your loop is just..
    for(;;) {
    PORTB ^= 1

    • @Fernando-du5uj
      @Fernando-du5uj Год назад

      Could you explain, please?

    • @wiktorwektor123
      @wiktorwektor123 11 месяцев назад

      @@Fernando-du5ujLearn boolean logic.

    • @argvSys
      @argvSys 5 месяцев назад

      Buen protip

    • @SpeccyMan
      @SpeccyMan 2 месяца назад

      The empty for() is ugly, use a while() loop!

  • @m.gredemptor2610
    @m.gredemptor2610 10 месяцев назад

    Works with sinulIDE too, very nice now I can practice my software even without hardware.

  • @pup4301
    @pup4301 3 года назад

    You are the best! Been trying to figure this out all day.

  • @ryan44662
    @ryan44662 Год назад

    I got the same immediate reaction from how high-level the arduino IDE felt.

  • @KingJellyfishII
    @KingJellyfishII 3 года назад +12

    Gotta nitpick a little sorry, -Os is technically optimise for space, but also speed but not as much as -O2

    • @LowLevel-TV
      @LowLevel-TV  3 года назад +5

      Thanks for the additional input! I'm bound to mess something up, haha.

    • @AlessioSangalli
      @AlessioSangalli 2 года назад +1

      Eheh I was scanning the comments for this. Great job anyway 👍

  • @youtubeviewer7077
    @youtubeviewer7077 7 месяцев назад

    Tip: you don't have to run those serial port commands as root if you add yourself to the 'dialout' group.

  • @yassinouakka126
    @yassinouakka126 Год назад +1

    you should do a video about the SAMD21 Cortex-M0+ MCU on the Arduino Nano 33 IoT, it would be cool since it uses a different architecture ✨✨

  • @chacmool2581
    @chacmool2581 2 года назад +2

    Great. But then the question becomes: if the advantage of Arduino is not having to go down to the bare metal and you nevertheless go down to it, why not dispense with the Arduino and work with other more powerful micro controllers?

    • @ruffrecords
      @ruffrecords 2 года назад

      The the great advantage of the Arduino for non-coders is the IDE. That is who the IDE was designed for and it enables then to do quite complex projects that would otherwise be beyond them. There is a whole range of different microcomputers in the Arduino range from little 8 bitters like the AVR right up to 32/64 bit ARM ones. so the range of possible projects even with the IDE is quite vast. But this is not the only way to create programs on the Arduino, and if you want the fastest most compact code in the lowest cost micro then the Arduino IDE is definitely not the best way. That is whn you need bare metal coding.

    • @ipodtouch470
      @ipodtouch470 Год назад

      @@ruffrecords Yes also using the Arduino IDE you really never learn true embedded programming you just get a small taste of it. This video is also great to teach people true embedded programming.

    • @ruffrecords
      @ruffrecords Год назад

      @@ipodtouch470 After a lot more digging and playing with the Arduino IDE I have how revised my opinion of it. The reason is that, as several people have discovered, you do not have to use the built in setup() and loop() paradigm. You can easily recreate the project shown in the above video simply by opening a new sketch and deleting the entire contents of the sketch and replacing it with a regular main() al la regular C. The advantage of doing it this way is you do not have to worry about installing a bunch of other files or creating a make file and you retain the other advantages of the IDE including code completion.

  • @uwubakaoniichan1637
    @uwubakaoniichan1637 10 месяцев назад

    Love this!

  • @catandtheostrich
    @catandtheostrich 5 месяцев назад

    Wait a minute. Are you using vi, and aligning with tabs? Evil! :))

  • @nils6948
    @nils6948 6 месяцев назад

    Great video, but there is one thing i don't understand. Where is the "PORTB" coming from? Obviously from the library "avr/io.h", but when i want to use a different programming language where i don't have that library i must be able to program my MCU right?
    Are you planing to do a tutorial that low level or can someone guide me to one if it's already exist?

  • @simonhelleman8714
    @simonhelleman8714 4 года назад +3

    Great video! Does the Arduino bootloader still need to be flashed to the board? Where does that fit into this architecture?

    • @LowLevel-TV
      @LowLevel-TV  4 года назад +1

      Thank you!
      The way that you're able to flash the board over USB is via the Arduino bootloader that's baked into board by default, so no you don't need to flash it again.
      If you were to overwrite the area in memory where the bootloader lives, you would need to flash the board using a ICSP programmer. I'll be making a video on that in the coming weeks.
      Thanks for watching!

  • @donythomas7
    @donythomas7 2 года назад +2

    Can you add a tutorial on how to program a blank atmega328p (no bootloader) using avr-gcc and avrdude?

  • @AugustineAriola
    @AugustineAriola 3 года назад +1

    I enjoyed this video pretty well. I think I need to go down low with AVR. I just got a book that will help me a great deal. I want to be proficient in AVR.
    Thank you

    • @udeolisachukwudalu1309
      @udeolisachukwudalu1309 3 года назад

      Pls help me with the book. Thanks

    • @AugustineAriola
      @AugustineAriola 3 года назад

      @@udeolisachukwudalu1309
      Where is your location? If you're in Imo state, come down to Imo State University Owerri. You can as well learn tons of other useful microcontroller projects. Thank you.

  • @DamianReloaded
    @DamianReloaded 7 месяцев назад

    Awesome. Thanks!

  • @Banikan
    @Banikan 10 месяцев назад

    Great tutorial! Just wondering why the compilation and linking needs to be split up in two steps? Could the same be achieved with only one call to avr-gcc?

  • @COTidus
    @COTidus 10 месяцев назад

    Really great tutorial, first time i have seen how to actually write C and flash it onto the microcontroller. However, there is something still unclear… If i wanted to have, lets say, 2 leds, on 2 different ports, would the writing to the PORTB be exclusive? like, if i say PORTB = PORTB | (1

    • @SpeccyMan
      @SpeccyMan 2 месяца назад

      Port B is an 8 bit port (bits 0 to 7) so there is no PORTB13!

  • @renechawy
    @renechawy 3 года назад

    Mr.LLL, will be great addition if you can share your GITHUB Repos, actually in general share to stumble users in GH to view your videos and be generate more traffic to excellent tutorial. WOW COOL

  • @leon-wp6su
    @leon-wp6su 2 года назад +1

    Useful work ! How I can find a reference manual about avr macro and avr functions ?

  • @qzorn4440
    @qzorn4440 Год назад

    very interesting & wonderful Arduino GPIO video info. I am wanting to have the Orange Pi-5 Debian-Bullseye Python GPIO PWM pins control the servo Pan/Tilt camera motors. Do you have any suggestions on how to use OPi.GPIO which is a drop-in for RPi.GPIO. I can get the GPIO pins on/off to work. However, PWM is giving me errors. Next, will be I2C on the OPi5. 🧐 Thank you.

  • @MD-vs9ff
    @MD-vs9ff 2 года назад +1

    CORRECTION: The avr-gcc -0s flag optimizes for SIZE, not speed.

    • @dan00b8
      @dan00b8 2 года назад

      yeah, was about to say

  • @Mayank-r2t
    @Mayank-r2t 2 месяца назад

    Sir actually i am planning to learn c first then after 7-8 months c++ so my question is that will i able to use arduino just after c language or i have to necessary wait till i lean c++ 😓😓

  • @RAndrewNeal
    @RAndrewNeal 2 года назад

    Fellow i3 user. Nice.

  • @dotaz8026
    @dotaz8026 Год назад

    Youuuuuu are Great!!!!!!!!! I love you :)))

  • @LukeAvedon
    @LukeAvedon 10 месяцев назад

    HELL YES!

  • @alexon2010
    @alexon2010 7 месяцев назад

    For you to understand how most explanations about Arduino are very bad, that's not the case in this video, I'm trying to understand the logic behind such PORB, PORTC..... I only found bad things, and in your video in part 11 :00 clearly shows what the design of the doors would be like and their reference relationship, most people don't understand why they don't present a drawing like yours, where can I get drawings like this

  • @RAndrewNeal
    @RAndrewNeal 2 года назад

    Seems like I need to actually learn C and how to write Makefiles before I can program microcontrollers. While I understood everything in the video, I don't know the bitwise notation nor do I know much about C syntax.

    • @SpeccyMan
      @SpeccyMan 2 месяца назад

      Did you ever ride a bicycle without first having to learn how to ride a bicycle?

  • @rainfallen1064
    @rainfallen1064 2 года назад +1

    what's the difference between DDRB = DDRB | (1

    • @rainfallen1064
      @rainfallen1064 2 года назад

      Oh, I get it now. It's so you can change that specific bit WITHOUT changing any other bit of DDRB.

    • @ruffrecords
      @ruffrecords 2 года назад

      They do but the first leaves all the other bits unchanged, the second does not. This is known as an unwanted side effect.

  • @rainfallen1064
    @rainfallen1064 2 года назад +1

    Would this tutorial work the same for elegoo uno atmega328p?

  • @sharpfang
    @sharpfang 2 года назад

    I like writing that as PORTB &=~ (1

  • @LubaLorenz
    @LubaLorenz 11 месяцев назад

    cool video)

  • @chrisalexthomas
    @chrisalexthomas 2 года назад

    Do arduinos have interrupts? I remember a little bit from x86 asm that you can use interrupts to run code periodically instead of doing nops on the cpu, so it's better performance and less battery.

    • @LowLevel-TV
      @LowLevel-TV  2 года назад +1

      They do! I have a video on AVR timer interrupts on the channel :)

    • @chrisalexthomas
      @chrisalexthomas 2 года назад

      @@LowLevel-TV I’m finding more and more videos. You’ve done a lot of really cool stuff, rust too?? Really? That’s pretty wild.

  • @pasiunepepaine
    @pasiunepepaine 3 года назад

    Cool! 👌

  • @simpasalsunda7244
    @simpasalsunda7244 11 месяцев назад

    thanks!

  • @guyarbel2387
    @guyarbel2387 2 года назад

    can you show the windows version ?
    I assume with msys2 not much will change.
    But still not sure what are the necessary changes.

  • @JoseLucasd
    @JoseLucasd Месяц назад

    why mine is saying that avr/io doesnt exist? i tried everything from many foruns but none worked :(

  • @samisiddiqi5411
    @samisiddiqi5411 2 года назад

    I personally like to use Geany, would the same principles apply there too?

  • @shivanshuraj7175
    @shivanshuraj7175 Год назад

    So how we are able to flash it?there is a flasher to do so ...?

  • @SheelByTorn
    @SheelByTorn 2 года назад

    Where can I learn this things? The resources in the internet for low-level programming like this is so limited

    • @ruffrecords
      @ruffrecords 2 года назад

      You are lucky to have the internet. When I started out programming micros back in the 70s the internet did not exist. The only information came from the data books supplied by the manufacturer. These days those same data sheets are now readily available on line. Nobody taught you haw to write assembly language and it was not until a decade later that C compilers for micros became available and I learnt C from a book too.

    • @filipesiegrist
      @filipesiegrist 2 года назад

      "Make: AVR Programming"
      is a great book and teached me all of this.

  • @prashanthkumar3834
    @prashanthkumar3834 9 месяцев назад

    I'm really unhappy coz you skipped the entire startup code, linker file etc

  • @filipesiegrist
    @filipesiegrist 2 года назад

    sadly you used the pre made arduino libraries. It would be better if you checked the datasheet registers values and created pointers to them.

    • @filipesiegrist
      @filipesiegrist 2 года назад

      but great video, though

    • @hidingsounds763
      @hidingsounds763 Год назад

      What arduino libraries he used?

    • @filipesiegrist
      @filipesiegrist Год назад

      @@hidingsounds763 AVR.h it seems

    • @SpeccyMan
      @SpeccyMan 2 месяца назад

      @@filipesiegrist That is NOT so! He actually used the avr-libc libraries which are part of the gcc avr toolchain.

  • @hudsonfinn7001
    @hudsonfinn7001 6 месяцев назад

    How would I do something similar with the Arduino R4 Wifi?

    • @oriebug2527
      @oriebug2527 5 месяцев назад

      it has a different architecture entirely ( arm architecture while most classic arduinos use the AVR architecture ) so you're gonna have to look up it's own tool chain and schematics

  • @dontknow8699
    @dontknow8699 Год назад

    I had an arduino mega2560 clone laying around, this is the make file that manages to flash to my board:
    default:
    avr-gcc -Os -DF_CPU=16000000UL -mmcu=atmega2560 led.c -o led.bin
    avr-objcopy -O ihex -R .eeprom led.bin led.hex
    avrdude -D -F -V -c avrispmkII -p ATMEGA2560 -P /dev/ttyACM0 -b 115200 -U flash:w:led.hex
    If I separate the avr-gcc command as shown in the video I get the following error:
    avr:6 architecture of input file `led.o' is incompatible with avr output
    If I use the arduino programmer (-c arduino) then I get the following error:
    avrdude: stk500_recv(): programmer is not responding
    avrdude: stk500_getsync() attempt 1 of 10: not in sync: resp=0x00
    If I use for example the avrispmkII programmer (I use this one as in the IDE it was selected by default) then I get the following error:
    avrdude: NOTE: "flash" memory has been specified, an erase cycle will be performed
    To disable this feature, specify the -D option.
    avrdude: erasing chip
    avrdude: stk500v2_command(): command failed
    Adding the -D flag solves the problem.
    I don't know why it works now. But it works fine at the time being.
    Trying to solve the issue I added my user to the group that appeared on the output of the following command: ls -l /tty/ACM0
    The code had to be modified because the pin that controls the LED is the PB7.
    Thank you for the video!
    EDIT:
    Reading the comments of the video I found the one by @Edgar Bonet, the make file could be further reduced as avrdude can read directly the ELF file:
    default:
    avr-gcc -Os -DF_CPU=16000000UL -mmcu=atmega2560 led.c -o led.bin
    avrdude -D -F -V -c avrispmkII -p ATMEGA2560 -P /dev/ttyACM0 -b 115200 -U flash:w:led.bin

    • @HuyNguyen-fq4rh
      @HuyNguyen-fq4rh Год назад

      thank you for your comment, the "avrispmkII " saved my day

  • @lucasjunghans40
    @lucasjunghans40 2 года назад

    Hey everybody,
    I'm so Stück right now. I work on Linux (I just switched recently, so I'm getting used to it).
    I have installed VSC and all the packages but somehow my header files cannot be opened by my program.
    I have absolutely no idea how to fix that... I guess there is some Path missing.
    Can someone please help me fixing this issue?
    I am so ready to learn more about low level programming but this problems drains all my enthusiasm out of my soul...

    • @lucasjunghans40
      @lucasjunghans40 2 года назад +1

      Update: in Case any other people reading this comment and having the same problem. Please use VIM. I just used it and it worked in like no time. So glad and excited to dig deeper now :)

  • @jasonc6241992
    @jasonc6241992 2 года назад

    Can you do a video like this for ESP32 ADC channels?

  • @kavorka8855
    @kavorka8855 3 года назад +1

    The idea behind Arduino is make it possible to design stuff without wasting time on how setup the system or worry about which registers should be loaded with what bits.
    Your question "did you learn anything by using Arduino" is meaningless. You could design amazing systems with Arduino. You could prototype anything relatively quickly with Arduino. All that without reading much data sheets.
    However, that doesn't mean one should not try programming Atnel MCUs with c, c++ or assembly.

    • @AlessioSangalli
      @AlessioSangalli 2 года назад +1

      If you think this, why did you watch this video about programming without the Arduino framework?

  • @rockstar1103
    @rockstar1103 Месяц назад

    Why not just use pointers?

  • @fcf8269
    @fcf8269 2 года назад

    Interesting to see what happens behind the scene, but I find amusing that you state that the Arduino IDE is not teaching you anything, while writing explicit code in C does.
    From educational standpoint this teach you how the controller send and received data; but in real case scenario where you use no IDE. you are forced to use machine code, not C.
    The IDE is there to simplify things for people that actually write code to use micro-controllers; it is not there to teach you how a specific micro-controller works, and nobody in their right state of mind working on hardware daily, would give up the IDE just to use C code to write explicit registry calls (unless you need to do that for specific purposes on-field; which is one in a million case).
    Ask any 6 figure developer if they would give up their IDE and write code on VI only, and most likely the answer is no; beside the old dinosaur that think that writing everything by hand is a display of his/her skills. When you have to deliver in a company you have no time for frivolities; you have to be fast, efficient and effective.
    So I think that your video is spot on from a training perspective, but it give away a strange sense of entitlement in thinking that there are common cases where you actually use this approach. There are cases of course, but are far in between, outside the academic context (and this is coming from someone that has been in the field for decades, so it is first hand experience). Knowing how to do it without an IDE and learning how the MC works is a great thing to do; but once you learn and get hired to produce, you rely heavily on manufacturer IDE or standard IDE for making your life easier. It is 2022 after all, so it is logic that progress make life easier and let you be more productive.
    Thanks for the very well made and interesting video

  • @instinctive9556
    @instinctive9556 2 месяца назад

    What happened

  • @Banikan
    @Banikan 8 месяцев назад

    I tried to expand on this by adding a function call to a function defined in my_lib.c and during the linking step I had to add the mmcu option, same as in the build and assemble step. Otherwise the program behaved very strangely with the led not blinking as intended.
    I.e. compile, assemble and link such:
    avr-gcc -Os -DF_CPU=16000000UL -mmcu=atmega328p -c -o led_driver.o led_driver.c
    avr-gcc -Os -DF_CPU=16000000UL -mmcu=atmega328p -c -o main.o main.c
    avr-gcc -mmcu=atmega328p -o main.elf main.o led_driver.o
    No idea why the mmcu option needs to be added when multiple objects files are added at the link step but is not needed when linking main alone..